package com.ccys.doctor.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ccys.common.constants.ContextConstants;
import com.ccys.common.enums.CodeEnum;
import com.ccys.common.third.gap.GapUtil;
import com.ccys.common.util.JedisUtil;
import com.ccys.common.util.Kit;
import com.ccys.common.vo.ResponseResultApp;
import com.ccys.common.vo.SuccessVo;
import com.ccys.db.cache.SysCodeUtil;
import com.ccys.db.cache.TranslationUtil;
import com.ccys.db.cache.UserUtil;
import com.ccys.db.dto.Where;
import com.ccys.db.dto.doctor.doctorConsultStage.*;
import com.ccys.db.dto.doctor.drugReaction.AllergiesMakeDto;
import com.ccys.db.dto.doctor.drugReaction.MedicationReactionsMakeDto;
import com.ccys.db.entity.*;
import com.ccys.db.mapper.*;
import com.ccys.db.page.AppPageDto;
import com.ccys.db.service.BookingBasicService;
import com.ccys.db.vo.doctor.doctorConsultStage.*;
import com.ccys.db.vo.doctor.patientHistory.BookingInfoVo;
import com.ccys.doctor.feignClient.NettyFeignService;
import com.ccys.doctor.service.DoctorConsultStageService;
import com.ccys.doctor.service.PatientHistoryService;
import com.github.pagehelper.PageInfo;
import feign.Request;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import tk.mybatis.mapper.entity.Example;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.stream.Collectors;

import static com.ccys.common.vo.ResponseResultApp.error;
import static com.ccys.common.vo.ResponseResultApp.success;

/**
 * @program: WhiteCoat
 * @description: DoctorConsultStage
 * @author: ZhangJingCan
 * @create: 2024-02-24 09:38
 **/

@Service
public class DoctorConsultStageServiceImpl implements DoctorConsultStageService {

    /**
     * 问诊开药记录
     */
    @Resource
    private TblBookingPrescriptionMapper tblBookingPrescriptionMapper;

    @Resource
    private TblBookingMapper tblBookingMapper;

    @Resource
    private TblBookingFeeMapper bookingFeeMapper;

    @Resource
    private TblBookingBenefitMapper bookingBenefitMapper;

    @Resource
    private TblMemberMapper tblMemberMapper;


    @Resource
    private TblProductListDetailMapper tblProductListDetailMapper;

    /**
     * 处方
     */
    @Resource
    private TblFavouritePrescriptionMapper tblFavouritePrescriptionMapper;

    @Resource
    private TblFavouritePrescriptionDetailMapper tblFavouritePrescriptionDetailMapper;

    @Resource
    private TblFavouritePrescriptionDetailProductMapper tblFavouritePrescriptionDetailProductMapper;

    /**
     * 问诊所有相关的package
     */
    @Resource
    private TblBookingPackageMapper tblBookingPackageMapper;

    /**
     * 问诊诊断表
     */
    @Resource
    private TblBookingDiagnosisMapper tblBookingDiagnosisMapper;

    /**
     * 患者地址
     */
    @Resource
    private TblMemberDeliveryAddressMapper tblMemberDeliveryAddressMapper;

    /**
     * 诊断表
     */
    @Resource
    private TblDiagnosisMapper tblDiagnosisMapper;

    @Resource
    private TblDoctorMapper tblDoctorMapper;

    @Resource
    private TblDoctorMessageTemplateMapper tblDoctorMessageTemplateMapper;

    @Resource
    private TblProductMapper tblProductMapper;

    @Resource
    private TblPurchaseOrderDetailMapper purchaseOrderDetailMapper;

    @Resource
    private TblBookingPatientAddressMapper patientAddressMapper;

    @Resource
    private TblFormulationQuantityTypeMapper formulationQuantityTypeMapper;

    @Resource
    private TblFormulationRouteDoctorMapper formulationRouteDoctorMapper;

    @Resource
    private TblBookingMedicalServiceMapper tblBookingMedicalServiceMapper;

    /**
     * 问诊患者的位置
     */
    @Resource
    private TblBookingPatientAddressMapper tblBookingPatientAddressMapper;

    /**
     * 问诊患者详细信息
     */
    @Resource
    private TblBookingMemberInfoMapper tblBookingMemberInfoMapper;

    /**
     * 问诊文字聊天记录
     */
    @Resource
    private TblBookingTextBasedMapper tblBookingTextBasedMapper;

    @Resource
    private TblBookingSummaryMapper tblBookingSummaryMapper;

    @Resource
    private TblProductGoApotikProductCompositionMapper tblProductGoApotikProductCompositionMapper;

    /**
     * Cdmp 购买记录
     */
    @Resource
    private TblCdmpBenefitMemberMapper tblCdmpBenefitMemberMapper;

    @Resource
    private TblPhqDetailResponseMapper tblPhqDetailResponseMapper;

    @Resource
    private TblPhqScoringMapper tblPhqScoringMapper;

    @Resource
    private TblMemberPhqResultMapper tblMemberPhqResultMapper;

    @Resource
    private TblMemberPhqResultDetailMapper tblMemberPhqResultDetailMapper;

    @Resource
    private TblPhqDetailMapper tblPhqDetailMapper;

    @Resource
    private TblThinkWellRecommendedCategoryMapper tblThinkWellRecommendedCategoryMapper;

    /**
     * 问诊视频开始结束时间记录
     */
    @Resource
    private TblBookingConsultationMapper tblBookingConsultationMapper;

    @Autowired
    private PatientHistoryService patientHistoryService;

    @Resource
    private TblMemberLongTermMapper tblMemberLongTermMapper;

    @Resource
    private TblBookingLongTermMapper tblBookingLongTermMapper;

    @Resource
    private TblBookingMedicationsMapper tblBookingMedicationsMapper;

    @Resource
    private TblBookingAllergiesMapper tblBookingAllergiesMapper;

    @Resource
    private TblMemberAllergiesMapper tblMemberAllergiesMapper;

    @Resource
    private TblUnitOfMeasurementMapper tblUnitOfMeasurementMapper;

    @Resource
    private TblDurationUnitMapper tblDurationUnitMapper;

    @Resource
    private TblPurchaseOrderDetailMapper tblPurchaseOrderDetailMapper;

    @Resource
    private TblBookingFeeMapper tblBookingFeeMapper;

    @Resource
    private TblBookingQueueMapper tblBookingQueueMapper;

    @Resource
    private TblEapBenefitMapper eapBenefitMapper;

    @Resource
    private TblCdmpBenefitMapper cdmpBenefitMapper;

    @Resource
    private TblPlanBenefitMapper planBenefitMapper;

    @Resource
    private TblPlanBenefitServiceMapper planBenefitServiceMapper;

    @Resource
    private TblBookingBenefitConsultMapper bookingBenefitConsultMapper;

    @Resource
    private TblDoctorRepeatMapper repeatMapper;

    @Resource
    private TblBookingDiagnosisLogMapper tblBookingDiagnosisLogMapper;

    @Autowired(required = false)
    private NettyFeignService nettyFeignService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp approveMedications(String bookingId) {
        if (ObjectUtil.isEmpty(bookingId)) {
            return error("Parameter abnormality");
        }
        Example example = new Example(TblBooking.class);
        example.createCriteria().andEqualTo("id", bookingId).andEqualTo("delFlag", 0);
        TblBooking booking = tblBookingMapper.selectOneByExample(example);
        if (ObjectUtil.isNull(booking)) {
            return error("Data does not exist");
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        tblBookingPrescriptionMapper.doctorApproveMedications(doctor.getId(), booking.getId());
        //todo 这里还需要保存log   EXEC [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 101, 0, null, null, @PRM_doctor_id
        //todo 这里还有一步未做，对方代码为：memberHelper.SetCultureInfo(bookingId); 目前没有memberHelper不知道这个方法做了什么
        //存入redis设置过期10分钟
        JedisUtil.setex("approveMedications:" + booking.getId(), ContextConstants.TEN_MINUTE_EXPIRATION, bookingId, CodeEnum.REDIS_BOOKING.codeOf());
        //todo 还需要一个推送 GetDeviceToPush(bookingId, accountId, (int)AccountType.Member,Constants.API_Notification_Type.PATIENT.DOCTOR_APPROVED_MEDICATIONS);
        //根据用户的语言做翻译
        String trans = TranslationUtil.getContentLastLanguage("success", tblMemberMapper.selectByPrimaryKey(booking.getMemId()).getCurrentLanguage());
        return ResponseResultApp.success(trans, new SuccessVo(true));
    }

    @Override
    public ResponseResultApp checkProductAvailableForIndonesia(CheckProductAvailableDto dto) {
        //如果没传入products,则无需做对比业务
        if (dto.getProducts().isEmpty()) {
            return success();
        }
        //查询当前病人所在经纬度
        TblBookingPatientAddress address = new TblBookingPatientAddress();
        address.setBookingId(dto.getBookingId());
        address.setDelFlag(0);
        address = patientAddressMapper.selectOne(address);
        if (address == null || address.getLatitude() == null || address.getLongitude() == null) {
            return error();
        }

        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBookingId());
        TblBookingFee fee = new TblBookingFee();
        fee.setBookingId(dto.getBookingId());
        fee.setDelFlag(0);
        fee = bookingFeeMapper.selectOne(fee);

        TblBookingBenefit bookingBenefit = new TblBookingBenefit();
        bookingBenefit.setBookingId(dto.getBookingId());
        bookingBenefit.setDelFlag(0);

        Integer aiaMember = (bookingBenefitMapper.selectCount(bookingBenefit) > 0) ? 1 : 0;

        //判断请求gap使用的id
        String gapId = GapUtil.getFeeId(fee.getIsSurcharge(),booking.getDoctorCountryOfResidence(),dto.getIs_waive_consultation_fee(),aiaMember);

        Map<String, Object> param = new HashMap<>();
        param.put("booking_id", dto.getBookingId());
        param.put("is_waive_consultation_fee", dto.getIs_waive_consultation_fee());
        param.put("products", dto.getProducts());
        param.put("latitude", address.getLatitude());
        param.put("longitude", address.getLongitude());
        Map<String, Object> consultation = new HashMap<>();
        consultation.put("product_master_id", gapId);
        param.put("consultation", consultation);
        String result = GapUtil.doctorValidateProducts(param);
        Boolean isSuccess = false;
        JSONObject response = new JSONObject();
        JSONObject data = new JSONObject();
        JSONObject merchant = new JSONObject();
        merchant.put("id", 0);
        merchant.put("merchant_name", null);
        response.put("match_products", new JSONArray());
        response.put("merchant", merchant);
        response.put("similar_products", new JSONArray());
        if (StringUtils.isNotBlank(result)) {
            try {
                JSONObject json = JSONObject.parseObject(result);
                json = json.getJSONObject("data");
                response.put("merchant", json.getJSONObject("merchant"));
                response.put("match_products", json.getJSONArray("match_products"));
                JSONArray similarProducts = json.getJSONArray("similar_products");
                if (similarProducts != null && !similarProducts.isEmpty()) {
                    for (Object s : similarProducts) {
                        JSONObject j = (JSONObject) s;
                        String composition = tblProductGoApotikProductCompositionMapper.findCompositionByGoApotikId(j.getString("id"));
                        j.put("name_only", j.getString("product_name"));
                        j.put("product_name", j.getString("product_name") + ((StringUtils.isNotBlank(composition)) ? composition : ""));
                        j.put("ingredient_only", composition);
                    }
                    response.put("similar_products", similarProducts);
                }
                isSuccess = true;
            } catch (Exception e) {

            }
        }
        data.put("isSuccess", isSuccess);
        data.put("response", response);
        return success(data);
    }


    @Override
    public ResponseResultApp checkQuantityForAddFavourite(CheckQuantityForAddFavouriteDto dto) {
        if (ObjectUtil.isEmpty(dto.getBookingId()) || ObjectUtil.isEmpty(dto.getMed_id()) || ObjectUtil.isEmpty(dto.getFavourite_id())) {
            return error("Parameter abnormality");
        }
        Example bookingExample = new Example(TblBooking.class);
        bookingExample.createCriteria().andEqualTo("id", dto.getBookingId()).andEqualTo("status", 3).andEqualTo("delFlag", 0);
        TblBooking booking = tblBookingMapper.selectOneByExample(bookingExample);
        if (ObjectUtil.isNull(booking)) {
            return error("Booking not found");
        }
        //校验医生
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        //如果不相等
        if (!doctor.getId().equals(booking.getDoctorId())) {
            return error("Booking not accessed");
        }
        //booking表的状态
        Integer status = booking.getStatus();
        Integer subStatus = booking.getSubStatus();
        if (status != 2 && status != 3) {
            if (status == 99) {
                return error("Booking completed");
            } else {
                //根据不同状态获取不同的返回message
                return error(getErrorBookingValid(status));
            }
        }
        if (status == 3 && subStatus == 1) {
            return error("Sorry, this consultation has been ended. Cannot update prescription.");
        }
        //查询是否生效
        if (booking.getPatientCountryOfResidence().equals("203")) {
            Integer count = tblFavouritePrescriptionDetailMapper.findCountByFavouriteIdAndByProductId(dto.getFavourite_id(), dto.getMed_id());
            if (count > 0) {
                return error("No medication found");
            }
        }

        TblProduct product = tblProductMapper.selectByPrimaryKey(dto.getMed_id());
        if (product == null || product.getDelFlag() == 1 || product.getStatus() != 1) {
            return error("Medication is invalid");
        }
        TblFormulationQuantityType quantityType = formulationQuantityTypeMapper.selectByPrimaryKey(product.getQuantityId());
        TblFormulationRouteDoctor routeDoctor = formulationRouteDoctorMapper.selectByPrimaryKey(quantityType.getFormulationDoctorId());

        //查询总共待开多少药
        int quantity = tblFavouritePrescriptionDetailProductMapper.selectSumByProductAndFavourite(dto.getMed_id(), dto.getFavourite_id());
        if (product.getQtyRemainInStock() < quantity) {
            //库存不足
            return error("The quantity of " + product.getName() + " in the inventory is not enough for your prescription.<br><strong>Quantity left = " + product.getQtyRemainInStock() + " " + routeDoctor.getName() + "</strong>");
        }

        //6个月内有过期的记录
        Calendar ca = Calendar.getInstance();
        ca.setTime(new Date());
        ca.add(Calendar.MONTH, 6);
        List<ProductExpireInTimeVo> expireList = purchaseOrderDetailMapper.selectExpireInTimeList(dto.getMed_id(), DateUtil.format(ca.getTime(), "yyyy-MM-dd"));
        if (!expireList.isEmpty()) {
            StringBuilder sb = new StringBuilder();
            sb.append("Expiry notice for ").append(product.getName()).append(".<br><br><strong>Expiring soon:</strong><ul>");
            for (ProductExpireInTimeVo v : expireList) {
                sb.append("<li>").append(v.getExpirationDate()).append(" - ").append(v.getQuantityRemain()).append(" ").append(routeDoctor.getName()).append("</li>");
            }
            sb.append("</ul>");
            return success(sb.toString(), null);
        }

        return success();
    }

    @Override
    public ResponseResultApp checkQuantityForRepeat(CheckQuantityForAddFavouriteDto dto) {
        //处理流程与上一个方法类似
        Example bookingExample = new Example(TblBooking.class);
        bookingExample.createCriteria().andEqualTo("id", dto.getCurrent_bookingId()).andEqualTo("status", 3).andEqualTo("delFlag", 0);
        TblBooking booking = tblBookingMapper.selectOneByExample(bookingExample);
        if (ObjectUtil.isNull(booking)) {
            return error("Booking not found");
        }
        //校验医生
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        //如果不相等
        if (!doctor.getId().equals(booking.getDoctorId())) {
            return error("Booking not accessed");
        }
        //booking表的状态
        Integer status = booking.getStatus();
        Integer subStatus = booking.getSubStatus();
        if (status != 2 && status != 3) {
            if (status == 99) {
                return error("Booking completed");
            } else {
                //根据不同状态获取不同的返回message
                return error(getErrorBookingValid(status));
            }
        }
        if (status == 3 && subStatus == 1) {
            return error("Sorry, this consultation has been ended. Cannot update prescription.");
        }

        TblBookingPrescription condition = new TblBookingPrescription();
        condition.setBookingId(dto.getBookingId());
        condition.setDelFlag(0);
        if (!dto.getIs_package()) {
            condition.setProductId(dto.getMed_id());
        } else {
            condition.setPackageId(dto.getMed_id());
        }
        List<TblBookingPrescription> pList = tblBookingPrescriptionMapper.select(condition);
        if (pList.isEmpty()) {
            return error("No medication found");
        }
        Map<String, Integer> pMap = new HashMap<>();
        for (TblBookingPrescription p : pList) {
            if (pMap.get(p.getProductId()) == null) {
                pMap.put(p.getProductId(), p.getQuantity());
            } else {
                pMap.put(p.getProductId(), p.getQuantity() + pMap.get(p.getProductId()));
            }
        }
        Set<String> keys = pMap.keySet();
        TblProduct product;
        StringBuilder sb = new StringBuilder();
        sb.append("<strong>Expiring soon:</strong><ul>");
        boolean expireFlag = false;
        for (String key : keys) {
            product = tblProductMapper.selectByPrimaryKey(key);
            if (product == null || product.getDelFlag() == 1 || product.getStatus() != 1) {
                return error("Medication is invalid");
            }
            TblFormulationQuantityType quantityType = formulationQuantityTypeMapper.selectByPrimaryKey(product.getQuantityId());
            TblFormulationRouteDoctor routeDoctor = formulationRouteDoctorMapper.selectByPrimaryKey(quantityType.getFormulationDoctorId());

            if (product.getQtyRemainInStock() < pMap.get(key)) {
                //库存不足
                return error("The quantity of " + product.getName() + " in the inventory is not enough for your prescription.<br><strong>Quantity left = " + product.getQtyRemainInStock() + " " + routeDoctor.getName() + "</strong>");
            }

            //6个月内有过期的记录
            Calendar ca = Calendar.getInstance();
            ca.setTime(new Date());
            ca.add(Calendar.MONTH, 6);
            List<ProductExpireInTimeVo> expireList = purchaseOrderDetailMapper.selectExpireInTimeList(key, DateUtil.format(ca.getTime(), "yyyy-MM-dd"));
            if (!expireList.isEmpty()) {
                expireFlag = true;
                for (ProductExpireInTimeVo v : expireList) {
                    sb.append("<li>").append(v.getExpirationDate()).append(" - ").append(v.getQuantityRemain()).append(" ").append(routeDoctor.getName()).append("</li>");
                }
                sb.append("</ul>");
            }
        }

        return success(expireFlag ? sb.toString() : "success", null);
    }

    @Override
    public ResponseResultApp<List<CheckQuantityMedicationInPackageVo>> checkQuantityMedicationInPackage(String packageId) {
        List<CheckQuantityMedicationInPackageVo> list = tblProductListDetailMapper.checkQuantityMedicationInPackage(packageId);
        return ResponseResultApp.success(list);
    }

    @Override
    public ResponseResultApp doctorDeletePrescriptions(DoctorDeletePrescriptionsDto dto) {
        //todo 进入的时候就要保存一个booking的log 对方代码：booking_bl.saveLog(model.bookingId, account_id, "", JsonConvert.SerializeObject(model), "DoctorDeletePrescriptions").ConfigureAwait(false);
//        tblFavouritePrescriptionMapper
        if (ObjectUtil.isEmpty(dto.getBookingId())) {
            return error("Parameter abnormality");
        }
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBookingId());
        if (ObjectUtil.isNull(booking)) {
            return error("Booking does not exist");
        }
        //检查问诊类型
        if (booking.getConsultationMode().equals(1)) {
            //todo 查看代码，觉得实际上下方不用那么多判断GP还是SP，只需要判断dto中参数存在则删除即可
            //在线问诊
            if (booking.getConsultType().equals(1)) {
                //准备删除的实体 - 软删除
                TblBookingPrescription prescription = new TblBookingPrescription();
                prescription.setDelFlag(1);
                //GP
                if (ObjectUtil.isNotEmpty(dto.getProductIds())) {
                    //条件
                    Example prescriptionExample = new Example(TblBookingPrescription.class);
                    prescriptionExample.createCriteria()
                            .andEqualTo("bookingId", booking.getId())
                            .andIn("thirdPartyProductId", dto.getProductIds())
                            .andEqualTo("delFlag", 0);
                    tblBookingPrescriptionMapper.updateByExampleSelective(prescription, prescriptionExample);
                }
                /**
                 * todo 函数：API_DeletePrescriptions
                 * 对方函数中还删除了一个
                 *     DELETE FROM [dbo].[tbl_Booking_ProductPackage] WHERE
                 *         booking_id = @PRM_booking_id AND
                 *         [type] = 'Medication' AND
                 *         package_id IN (
                 *             SELECT package_id FROM @PRM_package_prescriptions_to_be_deleted
                 *         )
                 *
                 *  todo 但是对方代码中并没有做出对应参数，对方代码如下：
                 *List<Prescription> model_package_prescriptions = new List<Prescription>();
                 *             apiResult = util.ConvertPrescription_Product_ListToDataTable(model_package_prescriptions);
                 *             DataTable package_prescription_dt;
                 *             if (apiResult.errorCode != ErrorCodes.OK)
                 *             {
                 *                 return apiResult;
                 *             }
                 *             else
                 *             {
                 *                 package_prescription_dt = (DataTable)apiResult.data;
                 *             }
                 */

                if (ObjectUtil.isNotEmpty(dto.getArrMedIds())) {
                    //条件
                    Example prescriptionExample = new Example(TblBookingPrescription.class);
                    prescriptionExample.createCriteria()
                            .andEqualTo("bookingId", booking.getId())
                            .andIn("productId", dto.getArrMedIds())
                            .andEqualTo("delFlag", 0);
                    tblBookingPrescriptionMapper.updateByExampleSelective(prescription, prescriptionExample);
                }
            } else {
                //SP
            }
        }
        return null;
    }

    @Override
    public ResponseResultApp<GetComboboxUpConsultVo> getComboboxUpConsult(String memberId, Integer pageIndex, Integer pageSize, String condition) {
        //最终返回对象
        GetComboboxUpConsultVo vo = new GetComboboxUpConsultVo();
        //开启分页
        AppPageDto.startPage();
        //查询历史咨询
        List<GetComboboxUpConsultVo.Consultants> consultants = tblBookingMapper.getComboboxUpConsultFindHistoryByMemberId(memberId, condition);
        PageInfo<GetComboboxUpConsultVo.Consultants> info = new PageInfo<>(consultants);
        vo.setTotalRow(info.getPages());
        vo.setConsultants(consultants);
        //查询问诊对应的诊断数据
        List<String> bookingIds = consultants.stream().map(GetComboboxUpConsultVo.Consultants::getId).collect(Collectors.toList());
        if (!bookingIds.isEmpty()) {
            List<GetComboboxUpConsultVo.Diagnosis> diagnosisList = tblBookingDiagnosisMapper.getComboboxUpConsultFindListByBookingIds(bookingIds);
            diagnosisList.forEach(item -> {
                //翻译诊断名称
                String trans = TranslationUtil.getContentLastLanguage(item.getMDDiagnosisName(), tblMemberMapper.selectByPrimaryKey(memberId).getCurrentLanguage());
                item.setTransName(StringUtils.isBlank(trans) ? item.getMDDiagnosisName() : trans);
            });
            //将诊断装入返回值
            vo.setDiagnosis(diagnosisList);
        }
        return ResponseResultApp.success(vo);
    }

    @Override
    public ResponseResultApp<GetDefaultAddressOfPatientVo> getDefaultAddressOfPatient(String memberId) {
        if (ObjectUtil.isEmpty(memberId)) {
            return error("Parameter abnormality");
        }
        TblMember member = tblMemberMapper.selectByPrimaryKey(memberId);
        if (ObjectUtil.isNull(member) || member.getDelFlag() == 1) {
            return error("Member does not exist");
        }
        GetDefaultAddressOfPatientVo vo = tblMemberDeliveryAddressMapper.getDefaultAddressOfPatient(memberId);
        return ResponseResultApp.success(vo);
    }

    @Override
    public ResponseResultApp<List<GetDiagnosisByCountryVo>> getDiagnosisByCountry(String searchText, String countryId) {
        //如果未传入国家ID - 默认203 - 新加坡
        if (ObjectUtil.isEmpty(countryId)) {
            countryId = "203";
        }
        List<GetDiagnosisByCountryVo> list = tblDiagnosisMapper.getDiagnosisByCountry(searchText, countryId);
        //开始翻译 todo 后续确认是否需要单独翻译，目前翻译字段取的一样的，查看对方的函数里面是写死的203国家和245
//        list.forEach(item->{
//
//        });
        return ResponseResultApp.success(list);
    }

    @Override
    public ResponseResultApp<List<GetDiagnosisExclusionVo>> getDiagnosisExclusion(String bookingId) {
        if (ObjectUtil.isEmpty(bookingId)) {
            return error("Parameter abnormality");
        }
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(bookingId);
        if (ObjectUtil.isEmpty(booking) || booking.getDelFlag() == 1) {
            return error("Booking does not exist");
        }

        //查询当前问诊是否有用到配套
        if (StringUtils.isBlank(booking.getBenefitId())) {
            return success();
        }

        String elId;
        if (5 == booking.getPlanType()) {
            //eap
            TblEapBenefit eapBenefit = eapBenefitMapper.selectByPrimaryKey(booking.getBenefitId());
            elId = eapBenefit.getDiagnosisExclusionListId();
        } else if (6 == booking.getPlanType()) {
            //cdmp
            TblCdmpBenefit cdmpBenefit = cdmpBenefitMapper.selectByPrimaryKey(booking.getBenefitId());
            elId = cdmpBenefit.getDiagnosisExclusionListId();
        } else {
            TblPlanBenefit planBenefit = planBenefitMapper.selectByPrimaryKey(booking.getBenefitId());
            elId = planBenefit.getDiagnosisExclusionListId();
        }
        TblPlanBenefitService benefitService = planBenefitServiceMapper.selectByPrimaryKey(booking.getBenefitServiceId());
        Long startTime = null;
        Long endTime = null;
        if (StringUtils.isNotBlank(benefitService.getRenewalDate())) {
            LocalDate date = LocalDate.now();
            ZoneId zoneId = ZoneId.of(booking.getBookingTimeZone());
            ZonedDateTime zonedDateTime = date.atStartOfDay(zoneId);
            String[] renewDateArr = benefitService.getRenewalDate().split("-");
            Integer renewMonth = Integer.valueOf(renewDateArr[0]);
            Integer renewDay = Integer.valueOf(renewDateArr[1]);
            if (zonedDateTime.getMonthValue() >= renewMonth && zonedDateTime.getDayOfMonth() > renewDay) {
                date = LocalDate.of(zonedDateTime.getYear() - 1, renewMonth, renewDay);
                zonedDateTime = date.atStartOfDay(zoneId);
                startTime = zonedDateTime.toInstant().toEpochMilli();
                date = LocalDate.of(zonedDateTime.getYear(), renewMonth, renewDay);
                zonedDateTime = date.atStartOfDay(zoneId);
                endTime = zonedDateTime.toInstant().toEpochMilli();
            } else {
                date = LocalDate.of(zonedDateTime.getYear(), renewMonth, renewDay);
                zonedDateTime = date.atStartOfDay(zoneId);
                startTime = zonedDateTime.toInstant().toEpochMilli();
            }
        }
        //计算时间内已使用的次数以及claim费用
        JSONObject amountJson = bookingBenefitConsultMapper.selectCalculateByCondition(booking.getMemId(), booking.getBenefitId(), startTime, endTime);
        BigDecimal claimAmount = amountJson.getBigDecimal("claimAmount");
        Integer visitCount = amountJson.getInteger("visitCount");
        claimAmount = claimAmount == null ? benefitService.getMaxAmountPerYear() : benefitService.getMaxAmountPerYear().subtract(claimAmount);
        visitCount = visitCount == null ? benefitService.getVisitCountLimit() : benefitService.getVisitCountLimit() - visitCount;

        List<GetDiagnosisExclusionVo> dataList = tblDiagnosisMapper.selectListByListId(elId);
        for (GetDiagnosisExclusionVo v : dataList) {
            v.setDiagnosicListId(elId);
            v.setMaxAmount(claimAmount);
            v.setVisitBalance(visitCount);
        }

        return ResponseResultApp.success(dataList);
    }

    @Override
    public ResponseResultApp<List<GetFavouritePrescriptionVo>> getFavouritePrescription() {
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        List<GetFavouritePrescriptionVo> list = tblDoctorMapper.getFavouritePrescription(doctor.getId());
        return ResponseResultApp.success(list);
    }

    @Override
    public ResponseResultApp<List<GetFavouritePresciptionDetailVo>> getFavouritePresciptionDetail(String favouritePrescriptionId) {
        //如果参数为空 - 直接返回成功 - 对方就这样的
        if (ObjectUtil.isEmpty(favouritePrescriptionId)) {
            return ResponseResultApp.success();
        }
        List<GetFavouritePresciptionDetailVo> list = tblFavouritePrescriptionMapper.getFavouritePresciptionDetail(favouritePrescriptionId);
        return ResponseResultApp.success(list);
    }

    @Override
    public ResponseResultApp<UpdateTextTemplateVo> updateTextTemplate(UpdateTextTemplateDto dto) {
        //校验
        if (ObjectUtil.isEmpty(dto.getId())) {
            return error("Parameter abnormality");
        }

        //模版名称不为空的情况，校验是否存在相同名称 - 排除自身
        if (ObjectUtil.isNotEmpty(dto.getTemplate_name())) {
            //先检查是否存在相同的模版名称
            Example checkExample = new Example(TblDoctorMessageTemplate.class);
            checkExample.createCriteria()
                    .andNotEqualTo("id", dto.getId())
                    .andEqualTo("name", dto.getTemplate_name())
                    .andEqualTo("delFlag", 0);
            int count = tblDoctorMessageTemplateMapper.selectCountByExample(checkExample);
            //如果存在就返回错误信息
            if (count > 0) {
                return error("Text Template already exists!");
            }
        }
        //否则开始修改
        TblDoctorMessageTemplate template = new TblDoctorMessageTemplate();
        template.setId(dto.getId());
        template.setName(dto.getTemplate_name() != null ? dto.getTemplate_name() : null);
        template.setMessage(dto.getTemplate_message() != null ? dto.getTemplate_message() : null);
        template.setIsDefault(dto.getIs_default() != null ? dto.getIs_default() : null);
        //设置状态
        Integer status = 0;
        if (ObjectUtil.isNotEmpty(dto.getStatus())) {
            if (dto.getStatus()) {
                status = 1;
            }
        }
        template.setStatus(status);
        //设置医生id
//        if (ObjectUtil.isEmpty(dto.getDoctor_id())){
//            dto.setDoctor_id(UserUtil.getCurLoginDoctor().getId());
//        }
//        template.setDoctorId(dto.getDoctor_id());
        //修改进数据库 - 这里只修改不为null的值
        tblDoctorMessageTemplateMapper.updateByPrimaryKeySelective(template);
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        //返回对象
        UpdateTextTemplateVo vo = new UpdateTextTemplateVo();
        AppPageDto.startPage();
        List<UpdateTextTemplateVo.TextTemplate> list = tblDoctorMessageTemplateMapper.findListByDoctorId(doctor.getId());
        PageInfo<UpdateTextTemplateVo.TextTemplate> info = new PageInfo<>(list);
        vo.setTotalPage(info.getPages());
        vo.setTextTemplates(list);
        return ResponseResultApp.success(vo);
    }

    @Override
    public ResponseResultApp updateStatusCoveredDiagnosis(UpdateStatusCoveredDiagnosisDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())
                || ObjectUtil.isEmpty(dto.getMedication_id())
                || ObjectUtil.isEmpty(dto.getShow_patient())
                || ObjectUtil.isEmpty(dto.getStatus())) {
            return error("Parameter abnormality");
        }
        if (!Kit.isInteger(dto.getStatus())) {
            return error("Parameter abnormality");
        }
        //转换
        Integer status = Integer.valueOf(dto.getStatus());
        //修改
        tblBookingPrescriptionMapper.updateStatusCoveredDiagnosis(dto.getBooking_id(), dto.getMedication_id(), status);

        if (Kit.isInteger(dto.getShow_patient())) {
            if (Integer.valueOf(dto.getShow_patient()) == 1) {
                //todo 需要websocket
                /**
                 * if (model.show_patient == 1)
                 *             {
                 *                 System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
                 *                 {
                 *                     member_bl.CallWebSocket(model.booking_id, "doctor.updateprescription", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
                 *                 });
                 *                 threadSocket.IsBackground = true;
                 *                 threadSocket.Start();
                 *             }
                 */
            }
        }

        return ResponseResultApp.success();
    }


    @Override
    public ResponseResultApp updatePrescriptionForApproval(UpdatePrescriptionForApprovalDto dto) {
//         if (ObjectUtil.isNotEmpty(dto.getPrescriptions())){
//             List<UpdatePrescriptionForApprovalDto.Prescription> list = dto.getPrescriptions().stream().filter(UpdatePrescriptionForApprovalDto.Prescription::getIs_package).collect(Collectors.toList());
//             if (!list.isEmpty()){
//
//             }
//         }
        Example bookingExample = new Example(TblBooking.class);
        bookingExample.createCriteria()
                .andEqualTo("id", dto.getBooking_id())
                .andEqualTo("delFlag", 0);
        TblBooking booking = tblBookingMapper.selectOneByExample(bookingExample);
        if (ObjectUtil.isEmpty(booking)) {
            return error("Booking not found");
        }

        if (booking.getStatus() == 3 && booking.getSubStatus() == 1) {
            return error("Sorry, this consultation has been ended. Cannot update prescription.");
        }

        //todo 缺少几个数量校验


//        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (ObjectUtil.isNotEmpty(dto.getPrescriptions())) {
            for (UpdatePrescriptionForApprovalDto.Prescription prescription : dto.getPrescriptions()) {
                TblBookingPrescription db = new TblBookingPrescription();
                db.setId(prescription.getId() != null ? prescription.getId() : null);
                db.setStepDose(prescription.getStep_dose() != null ? prescription.getStep_dose() : null);
                db.setQuantity(prescription.getQuantity() != null ? prescription.getQuantity().intValue() : null);
                db.setPatientInstruction(prescription.getPatient_instruction() != null ? prescription.getPatient_instruction() : null);
                db.setPharmacistInstruction(prescription.getPharmacist_instruction() != null ? prescription.getPharmacist_instruction() : null);
                db.setFrequency(prescription.getFrequency() != null ? prescription.getFrequency() : null);
                db.setWhatIsFor(prescription.getWhat_is_for() != null ? prescription.getWhat_is_for() : null);
                db.setCausesDrowsiness(prescription.getCauses_drowsiness() != null && prescription.getCauses_drowsiness().equals("false") ? 0 : (prescription.getCauses_drowsiness() != null ? 1 : null));
                db.setAnyPrecautions(prescription.getAny_precautions() != null ? prescription.getAny_precautions() : null);
                db.setDuration(prescription.getDuration() != null ? prescription.getDuration().intValue() : null);
                db.setDurationUnit(prescription.getDuration_unit() != null ? prescription.getDuration_unit() : null);
                db.setUomQuantity(prescription.getUom_quantity() != null ? prescription.getUom_quantity().intValue() : null);
                db.setPrn(prescription.getPrn() != null ? prescription.getPrn() ? 1 : 0 : null);
                db.setPrnReason(prescription.getPrn_reason() != null ? prescription.getPrn_reason() : null);
                db.setPrescriptionUnit(prescription.getUnit() != null ? String.valueOf(prescription.getUnit()) : null);
                db.setPackageId(prescription.getPackage_id() != null ? prescription.getPackage_id() : null);
                db.setTakeCompleteCourse(prescription.getTake_complete_course() != null ? prescription.getTake_complete_course() ? 1 : 0 : null);
                db.setRemark(prescription.getRemark() != null ? prescription.getRemark() : null);
                db.setProductId(prescription.getProduct_id() != null ? prescription.getProduct_id() : null);
                db.setIndexOrder(prescription.getIndex_order() != null ? prescription.getIndex_order() : null);
                db.setCoveredDiagnosis(prescription.getIs_covered_diagnosis() != null ? prescription.getIs_covered_diagnosis() ? 1 : 0 : null);
                db.setRemindPatientToCallBack(prescription.getRemind_patient_to_call_back() != null ? prescription.getRemind_patient_to_call_back() ? 1 : 0 : null);
                tblBookingPrescriptionMapper.updateByPrimaryKeySelective(db);
            }
        }

        //todo 还需要websocket
        /**
         * System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         *             {
         *                 member_bl.CallWebSocket(model.booking_id, "doctor.updateprescriptionforapproval", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *             });
         *             threadSocket.IsBackground = true;
         *             threadSocket.Start();
         *             booking_bl.saveLog(model.booking_id, accountId,"", JsonConvert.SerializeObject(model), "UpdatePrescriptionForApproval").ConfigureAwait(false);
         */

        return ResponseResultApp.success();
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updatePrescription(UpdatePrescriptionDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("Parameter abnormality");
        }
        if (ObjectUtil.isEmpty(dto.getPrescriptions())) {
            return error("No prescription selected");
        }
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBooking_id());
        if (ObjectUtil.isNull(booking) || booking.getDelFlag() == 1) {
            return error("Unable to find this booking");
        }
        if (booking.getStatus() != 3 && booking.getSubStatus() != 2) {
            if (booking.getStatus() == 99) {
                return error("Booking Not Access");
            } else {
                return error(getErrorBookingValid(booking.getStatus()));
            }
        }
        if (booking.getStatus() == 3 && booking.getSubStatus() == 1) {
            return error("Sorry, this consultation has been ended. Cannot update prescription.");
        }

        //查询这个booking的所有已开药物 - 主要用于回退药物数量
        Example bookingPrescriptionExample = new Example(TblBookingPrescription.class);
        bookingPrescriptionExample.createCriteria()
                .andEqualTo("bookingId", dto.getBooking_id())
                .andEqualTo("delFlag", 0);
        List<TblBookingPrescription> dBBookingPrescription = tblBookingPrescriptionMapper.selectByExample(bookingPrescriptionExample);
        //根据药物id分组，并计算分组之后的药物的数量
        Map<String, Integer> sumByGroup = dBBookingPrescription.stream()
                .collect(Collectors.groupingBy(TblBookingPrescription::getProductId, Collectors.summingInt(TblBookingPrescription::getQuantity)));
        //查询之前booking开的药物数据
        List<String> productIds = dBBookingPrescription.stream()
                .map(TblBookingPrescription::getProductId) // 获取所有的productId
                .distinct() // 去重
                .collect(Collectors.toList()); // 转换为List集合
        Example byProductIds = new Example(TblProduct.class);
        byProductIds.createCriteria()
                .andIn("id", productIds);
        //后面需要保存进数据库
        List<TblProduct> productByIds = tblProductMapper.selectByExample(byProductIds);
        //回退数量
        for (TblProduct productById : productByIds) {
            //当前循环药物需要回退的数量
            Integer number = sumByGroup.get(productById.getId());
            if (ObjectUtil.isNotEmpty(number)) {
                productById.setQtyRemainInStock(productById.getQtyRemainInStock() + number);
            }
            //修改进数据库
            tblProductMapper.updateByPrimaryKeySelective(productById);
        }

        //软删除 - 放在最后
        List<String> bookingPrescriptionIds = dBBookingPrescription.stream().map(TblBookingPrescription::getId).collect(Collectors.toList());
        tblBookingPrescriptionMapper.updateDelFlagByIds(bookingPrescriptionIds);

        //检查药物是否存在
        List<String> ids = dto.getPrescriptions().stream().map(UpdatePrescriptionDto.Prescription::getId).collect(Collectors.toList());
        Example productExample = new Example(TblProduct.class);
        productExample.createCriteria()
                .andIn("id", ids)
                .andEqualTo("status", 1)
                .andEqualTo("delFlag", 0);
        List<TblProduct> tblProducts = tblProductMapper.selectByExample(productExample);
        //这里校验了是否相等 - 因为对方传入的是一个集合的药物信息，这里也查询list，并且校验size是否相等
        if (ObjectUtil.isEmpty(tblProducts) || ids.size() != tblProducts.size()) {
            return error("Medication Not Found");
        }

        // 使用Stream转换为Map
        Map<String, UpdatePrescriptionDto.Prescription> map = dto.getPrescriptions().stream()
                .collect(Collectors.toMap(UpdatePrescriptionDto.Prescription::getId, prescription -> prescription));

        //检查数量是否足够扣减
        for (TblProduct product : tblProducts) {
            UpdatePrescriptionDto.Prescription prescription = map.get(product.getId());
            if (ObjectUtil.isNotEmpty(prescription)) {
                Integer num = product.getQtyRemainInStock() - prescription.getQuantity().intValue();
                //只要有一个不够扣减 - 则回滚事务
                if (num < 0) {
                    // 手动回滚事务
                    TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
                    return error("Insufficient quantity of drugs");
                }
                //数量足够
                product.setQtyRemainInStock(num);
                //准备对象 - 添加数据库
                TblBookingPrescription pr = new TblBookingPrescription();
                pr.setProductId(prescription.getId());
                pr.setQuantity(prescription.getQuantity().intValue());
                pr.setPatientInstruction(prescription.getPatient_instruction());
                pr.setPharmacistInstruction(prescription.getPharmacist_instruction());
                pr.setFrequency(prescription.getFrequency());
                pr.setWhatIsFor(prescription.getWhat_is_for());
                pr.setCausesDrowsiness(prescription.getCauses_drowsiness() != null && prescription.getCauses_drowsiness().equals("false") ? 0 : (prescription.getCauses_drowsiness() != null ? 1 : null));
                pr.setAnyPrecautions(prescription.getAny_precautions());
                pr.setPrn(prescription.getPrn() != null ? prescription.getPrn() ? 1 : 0 : null);
                pr.setPrnReason(prescription.getPrn_reason() != null ? prescription.getPrn_reason() : "");
                pr.setPackageId(prescription.getPackage_id() != null ? prescription.getPackage_id() : null);
                pr.setTakeCompleteCourse(prescription.getTake_complete_course() != null ? prescription.getTake_complete_course() ? 1 : 0 : null);
                pr.setRemark(prescription.getRemark() != null ? prescription.getRemark() : "");
                pr.setIndexOrder(prescription.getIndex_order() != null ? prescription.getIndex_order() : null);
                pr.setCoveredDiagnosis(prescription.getIs_covered_diagnosis() != null ? prescription.getIs_covered_diagnosis() ? 1 : 0 : null);
                pr.setRemindPatientToCallBack(prescription.getRemind_patient_to_call_back() != null ? prescription.getRemind_patient_to_call_back() ? 1 : 0 : null);
                tblBookingPrescriptionMapper.insertSelective(pr);
            }
        }

        /**
         * todo 缺少websocket
         * if (model.status == 1){
         *                 System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         *                 {
         *                     member_bl.CallWebSocket(model.booking_id, "doctor.updateprescription", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *                 });
         *                 threadSocket.IsBackground = true;
         *                 threadSocket.Start();
         *
         *             }
         *             booking_bl.saveLog(model.booking_id, accountId, "", JsonConvert.SerializeObject(new { request = model, response = response }), "UpdatePrescription").ConfigureAwait(false);
         */

        return ResponseResultApp.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updateNricFin(UpdateNricFinDto dto) {
        //先校验参数
        if (ObjectUtil.isEmpty(dto.getBookingId()) || ObjectUtil.isEmpty(dto.getNricFin())) {
            return error("Parameter abnormality");
        }
        //检查booking是否存在 - 经查看对方并没有校验是否在问诊
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBookingId());
        if (ObjectUtil.isNull(booking) || booking.getDelFlag() == 1) {
            return error("Booking not found");
        }
        //校验这个身份证号是否存在
        Example memberExample = new Example(TblMember.class);
        memberExample.createCriteria()
                .andNotEqualTo("id", booking.getMemId())
                .andEqualTo("nricFin", dto.getNricFin())
                .andEqualTo("delFlag", 0);
        if (ObjectUtil.isNotEmpty(tblMemberMapper.selectOneByExample(memberExample))) {
            return error("<strong>" + dto.getNricFin() + "</strong> belongs to an existing user. Please end the consult and contact the Product team for help");
        }
        //不存在 - 开始修改member信息
        TblMember member = new TblMember();
        member.setId(booking.getMemId());
        member.setNricFin(dto.getNricFin());
        tblMemberMapper.updateByPrimaryKeySelective(member);
        return ResponseResultApp.success();
    }

    @Autowired
    private BookingBasicService bookingBasicService;

    /**
     * 医疗服务
     */
    @Resource
    private TblMedicalServiceMapper tblMedicalServiceMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updateMedicalService(UpdateMedicalServiceDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("Booking_id is mandatory");
        }
        if (ObjectUtil.isEmpty(dto.getMedical_services())) {
            return ResponseResultApp.error("Parameter abnormality");
        }
        //查询booking - 检查booking状态
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking) || booking.getDelFlag() == 1) {
            return ResponseResultApp.error("Unable to find this booking");
        }
        //检查是否在问诊中 - 处于问诊返回true - 这里取反
        if (!bookingBasicService.isInConsultation(booking.getId())) {
            return ResponseResultApp.error("The booking could not be found.");
        }
        List<UpdateMedicalServiceDto.MedicalServices> dtoMedicalServices = dto.getMedical_services();
        List<String> ids = dtoMedicalServices.stream().map(UpdateMedicalServiceDto.MedicalServices::getId).collect(Collectors.toList());
        //查询医疗服务
        Example medicalServiceExample = new Example(TblMedicalService.class);
        medicalServiceExample.createCriteria()
                .andIn("id", ids)
                .andEqualTo("status", 1)
                .andEqualTo("delFlag", 0);
        List<TblMedicalService> services = tblMedicalServiceMapper.selectByExample(medicalServiceExample);
        //这里只校验如果没查询出任何数据 - 就返回错误信息
        if (ObjectUtil.isEmpty(services)) {
            return ResponseResultApp.error("Medical Service not found.");
        }

        //软删除
        tblBookingMedicalServiceMapper.deleteByBookingId(booking.getId());

        //转换为map - 用于新增
        Map<String, UpdateMedicalServiceDto.MedicalServices> toMap = dtoMedicalServices.stream().collect(Collectors.toMap(UpdateMedicalServiceDto.MedicalServices::getId, m -> m));
        //获取出存在的医疗服务的id集合
        List<String> dbIds = services.stream().map(TblMedicalService::getId).collect(Collectors.toList());
        //循环并取用传入的值
        for (String id : dbIds) {
            UpdateMedicalServiceDto.MedicalServices medicalServices = toMap.get(id);
            if (ObjectUtil.isNotEmpty(medicalServices)) {
                TblBookingMedicalService bookingMedicalService = new TblBookingMedicalService();
                bookingMedicalService.setBookingId(booking.getId());
                //有可能有问题，如果传入的不是数字会异常
                bookingMedicalService.setServiceType(Integer.valueOf(medicalServices.getService_type()));
                bookingMedicalService.setPackageId(medicalServices.getPackage_id() != null ? medicalServices.getPackage_id() : null);
                bookingMedicalService.setTestPeriod(bookingMedicalService.getTestPeriod() != null ? bookingMedicalService.getTestPeriod() : null);
                bookingMedicalService.setTestPeriodUnit(bookingMedicalService.getTestPeriodUnit() != null ? bookingMedicalService.getTestPeriodUnit() : null);
                bookingMedicalService.setTempIndex(bookingMedicalService.getTempIndex() != null ? bookingMedicalService.getTempIndex() : null);
                //对方也是0
                bookingMedicalService.setStatus(0);
                bookingMedicalService.setShow(dto.getIs_show());
                tblBookingMedicalServiceMapper.insertSelective(bookingMedicalService);
            }
        }

        /**
         * todo websocket
         * if (model.is_show == 1)
         *                 {
         *                     System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
         *                     {
         *                         member_bl.CallWebSocket(model.booking_id, "doctor.updatemedicalservice", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *                     });
         *                     threadSocket.IsBackground = true;
         *                     threadSocket.Start();
         *                 }
         *                 //member_bl.CallWebSocket(model.booking_id, "doctor.updatemedicalservice", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         *                 booking_bl.saveLog(model.booking_id, account_id,"", JsonConvert.SerializeObject(model), "UpdateMedicalService").ConfigureAwait(false);
         *                 return response;
         */

        //开始做校验
//        if (1 == booking.getPlanType()) {
//            //AIA corporate
//
//        } else if (2 == booking.getPlanType()) {
//            //Direct corporate
//        } else if (3 == booking.getPlanType()) {
//            //AIA Healthshield
//        } else if (4 == booking.getPlanType()) {
//            //Subscription
//        } else if (5 == booking.getPlanType()) {
//            //EAP
//        } else if (6 == booking.getPlanType()) {
//            //CDMP
//        }

        return ResponseResultApp.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updateLocationForIndonesia(UpdateLocationForIndonesiaDto dto) {
        if (ObjectUtil.isEmpty(dto.getBookingId()) || ObjectUtil.isEmpty(dto.getLatitude()) || ObjectUtil.isEmpty(dto.getLongitude())) {
            return ResponseResultApp.error("Parameter abnormality");
        }
        //检查booking
        TblBookingPatientAddress address = tblBookingPatientAddressMapper.findByBookingId(dto.getBookingId());
        if (ObjectUtil.isEmpty(address)) {
            return ResponseResultApp.error("This booking was not found");
        }
        address.setLatitude(dto.getLatitude());
        address.setLongitude(dto.getLongitude());
        tblBookingPatientAddressMapper.updateByPrimaryKeySelective(address);
        return ResponseResultApp.success();
    }

    @Override
    public ResponseResultApp updateGender(UpdateGenderDto dto) {
        if (ObjectUtil.isEmpty(dto.getBookingId()) || ObjectUtil.isEmpty(dto.getGender())) {
            return ResponseResultApp.error("Parameter abnormality");
        }
        //查询 - 在sql中有现职 - 如果booking不存在也不会有
        TblBookingMemberInfo info = tblBookingMemberInfoMapper.findByBookingId(dto.getBookingId());
        if (ObjectUtil.isEmpty(info)) {
            return ResponseResultApp.error("This booking was not found");
        }
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(info.getBookingId());
        //检查当前booking是大人还是小孩
        if (1 == booking.getMemType()) {
            //大人
            info.setMemberGender(dto.getGender());
        } else {
            //小孩
            info.setChildGender(dto.getGender());
        }
        //修改进数据库
        tblBookingMemberInfoMapper.updateByPrimaryKeySelective(info);
        return ResponseResultApp.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updateDiagnosis(UpdateDiagnosisDto dto) {
        //校验基础参数
        if (ObjectUtil.isEmpty(dto.getBooking_id()) || ObjectUtil.isEmpty(dto.getDiagnosis())) {
            return ResponseResultApp.error("Parameter abnormality");
        }
        //检查booking
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking) || booking.getDelFlag() == 1) {
            return ResponseResultApp.error("This booking was not found");
        }
        //已完成的不能操作
        if (booking.getDoctorIsCompleted() == 1) {
            return ResponseResultApp.error("A Diagnosis has been already been saved for this Consultation. \\r\\nIf you wish to edit the Diagnosis, please go to the consultation in the History tab.");
        }

        //查找诊断
        List<UpdateDiagnosisDto.Diagnosis> list = dto.getDiagnosis();
        for (UpdateDiagnosisDto.Diagnosis diagnosis : list) {
            if (ObjectUtil.isNotEmpty(diagnosis.getName())) {
                TblDiagnosis db = tblDiagnosisMapper.findByLikeNameAndByCountryId(diagnosis.getName(), booking.getDoctorCountryOfResidence());
                //只操作存在的诊断数据
                if (ObjectUtil.isNotEmpty(db)) {
                    //开始添加
                    TblBookingDiagnosis tblBookingDiagnosis = new TblBookingDiagnosis();
                    tblBookingDiagnosis.setBookingId(booking.getId());
                    tblBookingDiagnosis.setDiagnosisId(db.getId());
                    tblBookingDiagnosis.setInExclusionList(diagnosis.getIs_in_exclusion_list() != null ? diagnosis.getIs_in_exclusion_list() ? 1 : 0 : null);
                    tblBookingDiagnosisMapper.insertSelective(tblBookingDiagnosis);
                }
            }
        }
        return ResponseResultApp.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updateConSultationNote(UpdateConSultationNoteDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return ResponseResultApp.error("The booking_id field is required.");
        }
        //查询booking
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return ResponseResultApp.error("The booking could not be found.");
        }
        //检查是否有权限
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return ResponseResultApp.error("Unauthorized requests");
        }
        //操作文字聊天 对方函数反正这样写的，实际上并没有操作什么，但是update了一下
        if (ObjectUtil.isNotEmpty(dto.getIs_follow_up_action())) {
            TblBookingTextBased textBased = tblBookingTextBasedMapper.findByBookingId(booking.getId());
            if (ObjectUtil.isNotEmpty(textBased)) {
                textBased.setFollowUpAction(dto.getIs_follow_up_action() != null ? dto.getIs_follow_up_action() ? 1 : 0 : null);
                tblBookingTextBasedMapper.updateByPrimaryKeySelective(textBased);
            }
        }
        TblBookingSummary summary = tblBookingSummaryMapper.findByBookingId(booking.getId());
        if (ObjectUtil.isEmpty(summary)) {
            return ResponseResultApp.error("Data error, please contact the administrator");
        }
        //检查字段值并修改进数据库 - 这里调用的方法不会修改为null的值
        summary.setPresentingComplaint(dto.getPresenting_complaint() != null ? dto.getPresenting_complaint() : null);
        summary.setManagement(dto.getMamagement() != null ? dto.getMamagement() : null);
        summary.setInstructionsToPatient(dto.getInstructions_to_patient() != null ? dto.getInstructions_to_patient() : null);
        summary.setPatientTemp(dto.getPatient_temp() != null ? dto.getPatient_temp() : null);
        summary.setPatientBp1(dto.getPatient_bp1() != null ? dto.getPatient_bp1() : null);
        summary.setPatientBp2(dto.getPatient_bp2() != null ? dto.getPatient_bp2() : null);
        summary.setPatientHr(dto.getPatient_hr() != null ? dto.getPatient_hr() : null);
        summary.setBasicCdmpInformation(dto.getBasic_cdmp_information() != null ? dto.getBasic_cdmp_information() : null);
        tblBookingSummaryMapper.updateByPrimaryKeySelective(summary);
        //todo 这里添加tbl_booking_diagnosis的未做，不知道怎么做，对方postman数据添加不进去

        return ResponseResultApp.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp<UpdateCompositionVo> updateComposition(UpdateCompositionDto dto) {
        if (ObjectUtil.isEmpty(dto.getProduct_id())) {
            return ResponseResultApp.error("Parameter abnormality");
        }
        //查询
        String str = tblProductGoApotikProductCompositionMapper.findCompositionByGoApotikId(dto.getProduct_id());
        //返回值
        UpdateCompositionVo vo = new UpdateCompositionVo();
        if (ObjectUtil.isEmpty(str)) {
            /**
             * todo
             * Stopwatch stopWatch = new Stopwatch();
             *             stopWatch.Start();
             *             // endpoint
             *             var url = $"{_host}{_version}/product/therapeutic/details?product_master_id={productId}";
             *             var restClient = new RestClient(url);
             *             var request = InitRequest(Method.GET);
             *             // execute
             *             var response = restClient.Execute(request);
             *             stopWatch.Stop();
             *             var executionDuration = stopWatch.ElapsedMilliseconds;
             *             LogRequest("GetProductDetail", "", url, response.Content, response.StatusCode.ToString(), bookingId, executionDuration);
             *             // success
             *             if (response.StatusCode != HttpStatusCode.OK)
             *             {
             *                 _logger.Error($"GetProductDetail url: {url}");
             *                 _logger.Error($"GetProductDetail response: {response.Content}");
             *                 return null;
             *             }
             *
             *             var result =
             *                 JsonConvert.DeserializeObject<IndonesiaGeneralResponseModel<List<GetProductDetailResponseModel>>>(
             *                     response.Content);
             *
             *                     然后保存在tblProductGoApotikProductCompositionMapper表，再返回
             *
             *   错误返回如下：
             *   "errorCode": 0,
             *     "message": "Success",
             *     "data": {
             *         "isExist": false,
             *         "productComposition": null
             *     }
             */
            return ResponseResultApp.success();
        } else {
            vo.setIsExist(true);
            vo.setProductComposition(str);
            return ResponseResultApp.success(vo);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp unEnrollCdmp(UnEnrollCdmpDto dto) {
        if (ObjectUtil.isEmpty(dto.getMemberId())) {
            return error("MemberId is a mandatory parameter");
        }
        Example example = new Example(TblCdmpBenefitMember.class);
        example.createCriteria()
                .andEqualTo("memId", dto.getMemberId())
                .andEqualTo("delFlag", 0);
        TblCdmpBenefitMember cdmpBenefitMember = tblCdmpBenefitMemberMapper.selectOneByExample(example);
        if (ObjectUtil.isEmpty(cdmpBenefitMember)) {
            return error("Data does not exist");
        }
        cdmpBenefitMember.setStatus(0);
        tblCdmpBenefitMemberMapper.updateByPrimaryKeySelective(cdmpBenefitMember);
        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp submitPHQ4(SubmitPHQ4Dto dto) {
        Integer scoreSum = 0;
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        if (ObjectUtil.isNotEmpty(dto.getResponses())) {
//            sum = tblPhqDetailResponseMapper.findSumByIds(dto.getResponses());
            Example phqDetailResponseExample = new Example(TblPhqDetailResponse.class);
            phqDetailResponseExample.createCriteria()
                    .andIn("id", dto.getResponses())
                    .andEqualTo("delFlag", 0);
            List<TblPhqDetailResponse> tblPhqDetailResponses = tblPhqDetailResponseMapper.selectByExample(phqDetailResponseExample);
            // 使用 Stream API 计算 score 总和
            scoreSum = tblPhqDetailResponses.stream()
                    .filter(Objects::nonNull) // 过滤掉为 null 的对象
                    .mapToInt(response -> response.getScore() != null ? response.getScore() : 0) // 将 score 映射为 int 值，null 值视为 0
                    .sum();

            String scoringTypeStr = tblPhqScoringMapper.findScoringTypeStr(dto.getResponses(), scoreSum);
            TblMemberPhqResult memberPhqResult = new TblMemberPhqResult();
            memberPhqResult.setMemberId(dto.getMember_id());
            memberPhqResult.setDoctorId(doctor.getId());
            memberPhqResult.setBookingId(dto.getBooking_id());
            memberPhqResult.setTotalScore(scoreSum);
            memberPhqResult.setActionType(1);
            memberPhqResult.setScoringResult(scoringTypeStr);
            //添加
            tblMemberPhqResultMapper.insertSelective(memberPhqResult);

            //下方需要使用的数据组装
            List<SubmitPHQ4ScoreAndOrder> scoreAndOrders = new ArrayList<>();

            //循环添加detail
            for (TblPhqDetailResponse respons : tblPhqDetailResponses) {
                Example phqDetailExample = new Example(TblPhqDetail.class);
                phqDetailExample.createCriteria()
                        .andEqualTo("id", respons.getId())
                        .andEqualTo("delFlag", 0);
                TblPhqDetail tblPhqDetail = tblPhqDetailMapper.selectOneByExample(phqDetailExample);
                //组装数据 - 循环之外需要使用
                SubmitPHQ4ScoreAndOrder order = new SubmitPHQ4ScoreAndOrder();
                order.setScore(respons.getScore());
                order.setDispOrder(tblPhqDetail.getDispOrder());
                scoreAndOrders.add(order);

                //准备数据保存detail
                TblMemberPhqResultDetail detail = new TblMemberPhqResultDetail();
                detail.setResultId(memberPhqResult.getId());
                detail.setResponseId(respons.getId());
                detail.setResponseName(respons.getName());
                detail.setResponseScore(respons.getScore());
                detail.setQuestionName(tblPhqDetail.getQuestionName());
                tblMemberPhqResultDetailMapper.insertSelective(detail);
            }

            if (tblPhqDetailResponses.size() == 4) {
                //用于计算
                Integer numberOne = 0;
                Integer numberTwo = 0;
                for (SubmitPHQ4ScoreAndOrder order : scoreAndOrders) {
                    //根据不同判断做不同的计算
                    if (1 == order.getDispOrder() || 2 == order.getDispOrder()) {
                        numberOne += order.getScore() != null ? order.getScore() : 0;
                    }
                    if (3 == order.getDispOrder() || 4 == order.getDispOrder()) {
                        numberTwo += order.getScore() != null ? order.getScore() : 0;
                    }
                }
                //查询未删除的分类数据
                List<TblThinkWellRecommendedCategory> categories = tblThinkWellRecommendedCategoryMapper.findListByDelFlagFalse();
                //根据名称转换为map - 方便取用
                Map<String, TblThinkWellRecommendedCategory> toMap = categories.stream().collect(Collectors.toMap(TblThinkWellRecommendedCategory::getName, tblThinkWellRecommendedCategory -> tblThinkWellRecommendedCategory));
                //用于接收从map中取出的对象
                List<TblThinkWellRecommendedCategory> getMap = new ArrayList<>();
                if (numberOne < 3 && numberTwo < 3) {
                    TblThinkWellRecommendedCategory map = toMap.get("General");
                    if (ObjectUtil.isNotEmpty(map)) {
                        getMap.add(map);
                    }
                } else if (numberOne >= 3 && numberTwo < 3) {
                    TblThinkWellRecommendedCategory anxiety = toMap.get("Anxiety");
                    TblThinkWellRecommendedCategory general = toMap.get("General");
                    if (ObjectUtil.isNotEmpty(anxiety)) {
                        getMap.add(anxiety);
                    }
                    if (ObjectUtil.isNotEmpty(general)) {
                        getMap.add(general);
                    }
                } else if (numberOne < 3 && numberTwo >= 3) {
                    TblThinkWellRecommendedCategory general = toMap.get("General");
                    if (ObjectUtil.isNotEmpty(general)) {
                        getMap.add(general);
                    }
                    TblThinkWellRecommendedCategory depression = toMap.get("Depression");
                    if (ObjectUtil.isNotEmpty(depression)) {
                        getMap.add(depression);
                    }
                } else if (numberOne >= 3 && numberTwo >= 3) {
                    TblThinkWellRecommendedCategory general = toMap.get("General");
                    if (ObjectUtil.isNotEmpty(general)) {
                        getMap.add(general);
                    }
                    TblThinkWellRecommendedCategory depression = toMap.get("Depression");
                    if (ObjectUtil.isNotEmpty(depression)) {
                        getMap.add(depression);
                    }
                    TblThinkWellRecommendedCategory anxiety = toMap.get("Anxiety");
                    if (ObjectUtil.isNotEmpty(anxiety)) {
                        getMap.add(anxiety);
                    }
                }
                //对方写死的这个值
                String not = "Doc input";

                //拼接name
                String joinedNames = getMap.stream()
                        .map(TblThinkWellRecommendedCategory::getName)
                        .collect(Collectors.joining(","));
                memberPhqResult.setRecommendedContent(joinedNames);
                //准备utc时间
                Date utcDate = new Date();
                try {
                    utcDate = Kit.getUtcDate();
                } catch (ParseException e) {
                    throw new RuntimeException(e);
                }
                memberPhqResult.setRecommendedContentCreatedOnUtc(utcDate);
                memberPhqResult.setRecommendedContentNote(not);
                //修改进数据库
                tblMemberPhqResultMapper.updateByPrimaryKeySelective(memberPhqResult);

                //拼接id
//                String ids = getMap.stream()
//                        .map(TblThinkWellRecommendedCategory::getId)
//                        .collect(Collectors.joining(","));
                /**
                 * todo 对方还做了最后一个操作，目前我们没有这个表，不做操作
                 * IF(ISNULL(@PRM_category_ids,'') != '')
                 * 	BEGIN
                 * 		INSERT INTO [dbo].[tbl_Thinkwell_Data_Tracking_PHQ4_Category]
                 * 				   ([id]
                 * 				   ,[phq_result_id]
                 * 				   ,[recommended_category_id])
                 * 	   Select NEWID(),@PRM_result_id,Result from dbo.fnSplitString(@PRM_category_ids,',')
                 */
            }
        }

        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp startTBTCConsulation(StartTBTCConsulationDto dto) throws ParseException {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking) || booking.getDelFlag() == 1) {
            return error("Booking invalid");
        }
        if (3 != booking.getAppointmentType()) {
            return error("Booking Not Access");
        }
        if (booking.getStatus() < 1 || booking.getStatus() > 3) {
            return error(getErrorBookingValid(booking.getStatus()));
        }
        //开始业务
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        //检查是否为空 - 并设置
        if (ObjectUtil.isEmpty(booking.getClinicId()) && ObjectUtil.isNotEmpty(doctor.getCurrentClinicId())) {
            booking.setClinicId(doctor.getCurrentClinicId());
        }
        if (ObjectUtil.isEmpty(booking.getCompanyId()) && ObjectUtil.isNotEmpty(doctor.getCompanyId())) {
            booking.setCompanyId(doctor.getCompanyId());
        }
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        //用于判断
        boolean isNeedUpdate = false;
        //根据状态设置值
        if (booking.getStatus() == 1 && booking.getSubStatus() == -1) {
            booking.setStatus(2);
            booking.setSubStatus(1);
            tblBookingMapper.updateByPrimaryKeySelective(booking);
            //todo 这里有个bookingLog [dbo].[API_Insert_Booking_Log] @PRM_booking_id, @PRM_booking_status, @PRM_booking_sub_status, @driver_id
            isNeedUpdate = true;
        } else if (booking.getStatus() == 3) {
            //todo 这里有个bookingLog  [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 2, 0, null
            isNeedUpdate = true;
        } else if (booking.getStatus() == 1) {
            booking.setStatus(2);
            booking.setSubStatus(0);
            tblBookingMapper.updateByPrimaryKeySelective(booking);
            isNeedUpdate = true;
        }
        BookingInfoVo bookingInfoVo = null;
        if (isNeedUpdate) {
            //查询问诊视频开始结束时间记录
            TblBookingConsultation bookingConsultation = tblBookingConsultationMapper.findByBookingId(booking.getId());
            if (ObjectUtil.isNotEmpty(bookingConsultation)) {
                bookingConsultation.setBeginTime(new Date());
                bookingConsultation.setBeginTimeUtc(Kit.getUtcDate());
                bookingConsultation.setEndTime(new Date());
                bookingConsultation.setEndTimeUtc(Kit.getUtcDate());
                tblBookingConsultationMapper.updateByPrimaryKeySelective(bookingConsultation);
            }

            TblBookingMemberInfo memberInfo = tblBookingMemberInfoMapper.findByBookingId(booking.getId());
            if (ObjectUtil.isEmpty(memberInfo)) {
                //如果不存在 - 准备新增
                //先查询member数据
                TblMember member = tblMemberMapper.findById(booking.getMemId());
                if (ObjectUtil.isNotEmpty(member)) {
                    //保存的对象
                    TblBookingMemberInfo info = new TblBookingMemberInfo();
                    //开始设置值
                    info.setBookingId(booking.getId());
                    info.setMemberId(member.getId());
                    info.setMemberDob(member.getDob());
                    info.setMemberEmail(member.getEmail());
                    info.setMemberFirstName(member.getFirstName());
                    info.setMemberLastName(member.getLastName() != null ? member.getLastName() : null);
                    info.setMemberGender(member.getGender());
                    info.setMemberNricFin(member.getNricFin());
                    info.setMemberPhoneNumber(member.getPhone());
                    if (booking.getMemType() == 2) {
                        //到这里说明当前问诊是小孩 - 也需要查询小孩
                        TblMember child = tblMemberMapper.findById(booking.getPatientMemId());
                        info.setChildId(child.getId());
                        info.setChildDob(child.getDob());
                        info.setChildEmail(child.getEmail());
                        info.setChildFirstName(child.getFirstName());
                        info.setChildLastName(child.getLastName() != null ? child.getLastName() : null);
                        info.setChildGender(child.getGender());
                        info.setChildNricFin(child.getNricFin());
                        info.setChildPhoneNumber(child.getPhone() != null ? child.getPhone() : null);
                    }
                    tblBookingMemberInfoMapper.insertSelective(info);
                }
                //调用
                ResponseResultApp bookingInfo = patientHistoryService.getBookingInfo(booking.getId());

                if (ObjectUtil.isNotEmpty(bookingInfo.getData())) {
                    bookingInfoVo = (BookingInfoVo) bookingInfo.getData();
                }
                /**
                 * todo 这个未做
                 * System.Threading.Thread thread;
                 *                     thread = new System.Threading.Thread(delegate ()
                 *                     {
                 *                         BroadcastToCMS_ActiveConsultPage();
                 *                     });
                 *                     thread.IsBackground = true;
                 *                     thread.Start();
                 */
                if (ObjectUtil.isNotEmpty(booking.getPatientReady()) && booking.getPatientReady() == 0) {
                    SysCode code = null;
                    if (ObjectUtil.isNotEmpty(booking.getConsultationMode())) {
                        if (7 == booking.getConsultationMode()) {
                            code = SysCodeUtil.getCode("Textbased_Automated_Greeting_EatWell", "1");
                        } else {
                            code = SysCodeUtil.getCode("Textbased_Automated_Greeting_Tbtc", "2");
                        }
                        if (6 == booking.getConsultationMode()) {
                            /**
                             * todo 未做 tbtc_greeting 就是code里面的name
                             * Task.Run(() =>
                             *                         {
                             *                             try
                             *                             {
                             *                                 if (objResult.booking_consultation_type.ToString() == Constants.TEXT_BASED_CONSULTATION_TYPE.ToString())
                             *                                 {
                             *                                     string Tbtc_Greeting = objResult.tbtc_greeting.ToString();
                             *                                     string Uuid = $"doctor_{account_id}";
                             *
                             *                                     PubnubHelper _pubnub = new PubnubHelper(Uuid, booking_id);
                             *                                     _pubnub.Publish(Tbtc_Greeting, _pubnub.Welcome);
                             *                                     _pubnub.Unsubscribe();
                             *                                 }
                             *                             }
                             *                             catch (Exception)
                             *                             {
                             *
                             *                             }
                             *                         });
                             */
                        }
                    }
                }
            }
        }
        /**
         * todo 未做
         * member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.startconsulation", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
         * booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(model), "StartTBTCConsulation").ConfigureAwait(false);
         */
        return success(bookingInfoVo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp showPrescriptionToPatient(ShowPrescriptionToPatientDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.selectByPrimaryKey(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking) || booking.getDelFlag() == 1) {
            return error("Booking invalid");
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        //如果当前booking不属于当前医生 - 返回错误信息
        if (!booking.getDoctorId().equals(doctor.getId())) {
            return error("Booking Not Access");
        }
        if (booking.getStatus() == 3 && booking.getSubStatus() == 1) {
            return error("Sorry, this consultation has been ended. Cannot update prescription.");
        }
        if (1 == booking.getConsultationMode()) {
            tblBookingPrescriptionMapper.updateShowByBookingId(booking.getId());
            tblBookingPackageMapper.updateMedicationShowByBookingId(booking.getId());
        }

        if (1 == dto.getStatus()) {
            /**
             * todo websocket未做
             *  System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
             *                         {
             *                             member_bl.CallWebSocket(model.booking_id, "doctor.updateprescription", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
             *                         })
             *                         {
             *                             IsBackground = true
             *                         };
             *                         threadSocket.Start();
             */
        }

        //todo 保存日志：booking_bl.saveLog(model.booking_id, account_id, "", JsonConvert.SerializeObject(new { request = model, response = showResult }), "ShowPrescriptionToPatient").ConfigureAwait(false);

        this.nettyFeignService.sendMessageWebSocket(new Request.Options(6000, 6000), "bookingId","msg", "ok");
        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp<SendFirstTBTCMessageVo> sendFirstTBTCMessage(SendFirstTBTCMessageDto dto) throws ParseException {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("The booking_id field is required.");
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        //这个接口要查询bookingId并且对应当前医生ID！
        Example bookingExample = new Example(TblBooking.class);
        bookingExample.createCriteria()
                .andEqualTo("id", dto.getBooking_id())
                .andEqualTo("doctorId", doctor.getId())
                .andEqualTo("delFlag", 0);
        TblBooking booking = tblBookingMapper.selectOneByExample(bookingExample);
        if (ObjectUtil.isEmpty(booking)) {
            return error("Booking invalid");
        }
        //检查是否存在文字聊天的数据
        Example textBasedExample = new Example(TblBookingTextBased.class);
        textBasedExample.createCriteria()
                .andEqualTo("bookingId", booking.getId())
                .andEqualTo("delFlag", 0);
        TblBookingTextBased textBased = tblBookingTextBasedMapper.selectOneByExample(textBasedExample);
        //如果时间字段有值 - 返回错误信息
        if (ObjectUtil.isNotEmpty(textBased.getBeginChatDate())) {
            return error("Booking Not Access");
        }
        Date date = new Date();
        Date utcDate = Kit.getUtcDate();

        //判断数据是否存在 - 存在则修改 - 不存在则新增
        if (ObjectUtil.isNotEmpty(textBased)) {
            textBased.setBeginChatDate(date);
            textBased.setBeginChatDateUtc(utcDate);
            tblBookingTextBasedMapper.updateByPrimaryKeySelective(textBased);
        } else {
            TblBookingTextBased insert = new TblBookingTextBased();
            insert.setBookingId(booking.getId());
            insert.setBeginChatDate(date);
            insert.setBeginChatDateUtc(utcDate);
            tblBookingTextBasedMapper.insertSelective(insert);
        }
        tblBookingConsultationMapper.updateByDateAndUtcDateAndBookingId(date, utcDate, booking.getId());
        //返回值对象
        SendFirstTBTCMessageVo vo = new SendFirstTBTCMessageVo();
        //booking的id
        vo.setId(booking.getId());
        //当前时间
        vo.setConsultantBeginChatDate(date);
        return success(vo);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp saveLongTerm(SaveLongTermDto dto) {
        if (ObjectUtil.isEmpty(dto.getLongterm()) || ObjectUtil.isEmpty(dto.getAction())) {
            //为空不需要操作
            return success();
        }

        SaveLongTermDto.Longterm longterm = dto.getLongterm();
        if (dto.getAction().equals("ADD")) {
            //先删除
            if (ObjectUtil.isNotEmpty(longterm.getMember_id())) {
                tblMemberLongTermMapper.deleteByMemberIdAndByDiagnosisIdOrByProductId(longterm.getMember_id(), longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null, longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
                //准备保存
                TblMemberLongTerm memberLongTerm = new TblMemberLongTerm();
                memberLongTerm.setMemberId(longterm.getMember_id());
                memberLongTerm.setConditionId(longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null);
                memberLongTerm.setConditionName(longterm.getLongTerm_Condition_Name() != null ? longterm.getLongTerm_Condition_Name() : null);
                memberLongTerm.setNotes(longterm.getNotes() != null ? longterm.getNotes() : null);
                memberLongTerm.setLongtermType(longterm.getLongterm_type() != null ? longterm.getLongterm_type() : null);
                memberLongTerm.setMedicationId(longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
                memberLongTerm.setMedicationName(longterm.getLongterm_Medication_Name() != null ? longterm.getLongterm_Medication_Name() : null);
                memberLongTerm.setTreatedSince(longterm.getTreated_since() != null ? longterm.getTreated_since() : null);
                tblMemberLongTermMapper.insertSelective(memberLongTerm);

                //bookingId不为空才保存，不然没意义
                if (ObjectUtil.isNotEmpty(longterm.getBooking_id())) {
                    TblBookingLongTerm bookingLongTerm = new TblBookingLongTerm();
                    bookingLongTerm.setBookingId(longterm.getBooking_id());
                    bookingLongTerm.setConditionId(longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null);
                    bookingLongTerm.setConditionName(longterm.getLongTerm_Condition_Name() != null ? longterm.getLongTerm_Condition_Name() : null);
                    bookingLongTerm.setNotes(longterm.getNotes() != null ? longterm.getNotes() : null);
                    bookingLongTerm.setLongtermType(longterm.getLongterm_type() != null ? longterm.getLongterm_type() : null);
                    bookingLongTerm.setMedicationId(longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
                    bookingLongTerm.setMedicationName(longterm.getLongterm_Medication_Name() != null ? longterm.getLongterm_Medication_Name() : null);
                    bookingLongTerm.setTreatedSince(longterm.getTreated_since() != null ? longterm.getTreated_since() : null);
                    tblBookingLongTermMapper.insertSelective(bookingLongTerm);
                }

            }
        } else if (dto.getAction().equals("DELETE")) {
            if (ObjectUtil.isNotEmpty(longterm.getMember_id())) {
                tblMemberLongTermMapper.deleteByMemberIdAndByDiagnosisIdOrByProductId(longterm.getMember_id(), longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null, longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
            }
            if (ObjectUtil.isNotEmpty(longterm.getBooking_longterm_id())) {
                tblBookingLongTermMapper.updateDelflagTrue(longterm.getBooking_longterm_id());
            }
        } else if (dto.getAction().equals("UPDATE")) {
            //检查id存在才操作
            if (ObjectUtil.isNotEmpty(longterm.getBooking_longterm_id())) {
                TblBookingLongTerm bookingLongTerm = tblBookingLongTermMapper.findById(longterm.getBooking_longterm_id());
                if (ObjectUtil.isNotEmpty(bookingLongTerm)) {
                    bookingLongTerm.setConditionId(longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null);
                    bookingLongTerm.setConditionName(longterm.getLongTerm_Condition_Name() != null ? longterm.getLongTerm_Condition_Name() : null);
                    bookingLongTerm.setMedicationId(longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
                    bookingLongTerm.setMedicationName(longterm.getLongterm_Medication_Name() != null ? longterm.getLongterm_Medication_Name() : null);
                    bookingLongTerm.setNotes(longterm.getNotes() != null ? longterm.getNotes() : null);
                    bookingLongTerm.setTreatedSince(longterm.getTreated_since() != null ? longterm.getTreated_since() : null);
                    tblBookingLongTermMapper.updateByPrimaryKeySelective(bookingLongTerm);
                }
            }
            if (ObjectUtil.isNotEmpty(longterm.getMember_id())) {
                //不能确定是否为单个对象 - 所以用集合来
                List<TblMemberLongTerm> memberLongTerms = tblMemberLongTermMapper.findByMemberIdAndByDiagnosisIdOrByProductId(longterm.getMember_id(), longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null, longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
                memberLongTerms.forEach(item -> {
                    item.setConditionId(longterm.getLongTerm_Condition_ID() != null ? longterm.getLongTerm_Condition_ID() : null);
                    item.setConditionName(longterm.getLongTerm_Condition_Name() != null ? longterm.getLongTerm_Condition_Name() : null);
                    item.setNotes(longterm.getNotes() != null ? longterm.getNotes() : null);
                    item.setMedicationId(longterm.getLongterm_Medication_ID() != null ? longterm.getLongterm_Medication_ID() : null);
                    item.setMedicationName(longterm.getLongterm_Medication_Name() != null ? longterm.getLongterm_Medication_Name() : null);
                    item.setTreatedSince(longterm.getTreated_since() != null ? longterm.getTreated_since() : null);
                    tblMemberLongTermMapper.updateByPrimaryKeySelective(item);
                });
            }
        }

        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp saveDrugAlergy(SaveDrugAlergyDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return error("Booking invalid");
        }
        //todo 下方的所有软删除 - 对方都是直接删除的 - 并没有判断有值才操作 - 所以我们也直接不管是否传入新值都软删除掉所有数据
        //软删除问诊当前用药
        tblBookingMedicationsMapper.updateDelFlagTrueByBookingId(booking.getId());
        //如果传入了药品信息 - 开始保存药品
        if (ObjectUtil.isNotEmpty(dto.getMedications())) {
            //循环药品数据
            dto.getMedications().forEach(item -> {
                //对方代码是检查这个药品如果不存在才去新增
                if (ObjectUtil.isEmpty(tblProductMapper.findById(item.getId()))) {
                    TblProduct tblProduct = new TblProduct();
                    tblProduct.setId(item.getId());
                    tblProduct.setName(item.getName());
                    tblProduct.setDescription("free text allergy");
                    tblProduct.setStatus(1);
                    tblProductMapper.insertSelective(tblProduct);
                }
                /**
                 * todo 这里还有一个allergies  我们这个表不存在了，说的是不用管了
                 * 	  INSERT  INTO tbl_MD_Allergies (MDAllergies_ID,MDAllergies_name,MDAllergies_description,MDAllergies_is_show, MDAllergies_status) SELECT medication_id,name,N'free text allergy', 1, 1 from @PRM_medications  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Allergies f WHERE cr.medication_id = f.MDAllergies_ID)
                 */

                //准备数据保存问诊当前用药
                TblBookingMedications tblBookingMedications = new TblBookingMedications();
                tblBookingMedications.setBookingId(booking.getId());
                tblBookingMedications.setProductId(item.getId());
                //猜测应该使用 usage_period_id 但是对方取的usage_period 示例中两个字段的值也相同 - 这里跟着对方代码写的
                tblBookingMedications.setMedicationUsagePeriodId(item.getUsage_period() != null ? item.getUsage_period() : null);
                tblBookingMedications.setAmountOfDosage(item.getAmount_of_dosage());
                tblBookingMedicationsMapper.insertSelective(tblBookingMedications);
            });
        }

        //软删除  - 对方有个delete tbl_Booking_MedicationReaction where  booking_id=@booking_id
        //我们将tbl_Booking_MedicationReaction表整合在了tbl_booking_allergies中 - 所以这里就相当于全删除了
        tblBookingAllergiesMapper.updateDelFlagTrueByBookingId(booking.getId());
        //todo 后续需要确认是否正确
        String memberId = booking.getMemId();
        if (booking.getMemType() == 2) {
            memberId = booking.getPatientMemId();
        }
        //下方还有一个：Delete tbl_Member_MedicationReactions Where mem_id = @PRM_member_id
        //我们将tbl_Member_MedicationReactions合并至了tbl_member_allergies中 - 所以这个操作也删除光了的
        tblMemberAllergiesMapper.updateDelFlagTrueByMemberId(memberId);

        //如果传入了过敏
        if (ObjectUtil.isNotEmpty(dto.getAllergies())) {
            //循环传入的过敏数据
            for (AllergiesMakeDto item : dto.getAllergies()) {
                /**
                 * todo 这里有两步未做 - 对方将allergies存入了medication表中 - 对应我们的product表 - 但是我们不存入这个表 -所以这里忽略
                 * 另外我们没有tbl_allergies表了
                 * 	INSERT  INTO tbl_MD_Medication (MDMedication_ID,MDMedication_name,MDMedication_description,MDMedication_is_show, MDMedication_status) SELECT [allergy_id],name,N'free text allergy', 1, 1 from @PRM_allergies  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Medication f WHERE cr.[allergy_id] = f.MDMedication_ID)
                 * 	INSERT  INTO tbl_MD_Allergies (MDAllergies_ID,MDAllergies_name,MDAllergies_description,MDAllergies_is_show, MDAllergies_status) SELECT [allergy_id],name,N'free text allergy', 1, 1 from @PRM_allergies  cr WHERE NOT EXISTS ( SELECT 1 FROM tbl_MD_Allergies f WHERE cr.[allergy_id] = f.MDAllergies_ID)
                 */
                //判断allergiesName 必须不为空才添加 - 因为我们allergies表已经被删了，所以如果取id没意义 - 只需要存入name即可
                if (ObjectUtil.isNotEmpty(item.getName())) {
                    //准备添加数据
                    //bookingAllergies
                    TblBookingAllergies bookingAllergies = new TblBookingAllergies();
                    bookingAllergies.setAllergiesName(item.getName());
                    bookingAllergies.setBookingId(booking.getId());
                    tblBookingAllergiesMapper.insertSelective(bookingAllergies);

                    //memberAllergies
                    TblMemberAllergies memberAllergies = new TblMemberAllergies();
                    memberAllergies.setAllergiesName(item.getName());
                    memberAllergies.setMemId(memberId);
                    tblMemberAllergiesMapper.insertSelective(memberAllergies);
                }
            }
        }
        //校验medicationReaction - 存在则操作
        //对方的tbl_Booking_MedicationReaction 表 我们整合在了tbl_booking_allergies中
        if (ObjectUtil.isNotEmpty(dto.getMedication_reactions())) {
            for (MedicationReactionsMakeDto item : dto.getMedication_reactions()) {
                //这个id有值才操作
                if (ObjectUtil.isNotEmpty(item.getMem_medication_reaction_ID())) {
                    //bookingAllergies
                    TblBookingAllergies bookingAllergies = new TblBookingAllergies();
                    bookingAllergies.setBookingId(booking.getId());
                    bookingAllergies.setMedicationReactionId(item.getMD_MedicationReaction_ID());
                    tblBookingAllergiesMapper.insertSelective(bookingAllergies);


                    /**
                     * 下方操作，对方函数为：
                     * Delete tbl_Member_MedicationReactions Where mem_id = @PRM_member_id
                     *
                     * 		Insert Into tbl_Member_MedicationReactions([mem_id], [MD_MedicationReaction_ID], [mem_medication_reaction_index],mem_medication_other_reaction)
                     * 		Select Distinct @PRM_member_id, medication_reaction_id, [index],[otherreaction] From @PRM_medication_reactions
                     * 	todo:对方的tbl_Member_MedicationReactions表，我们合并至：tbl_member_allergies表中
                     */
                    //memberAllergies
                    TblMemberAllergies memberAllergies = new TblMemberAllergies();
                    memberAllergies.setMedicationReactionId(item.getMD_MedicationReaction_ID());
                    memberAllergies.setMemId(memberId);
                    tblMemberAllergiesMapper.insertSelective(memberAllergies);
                }
            }
        }
        //todo booking_bl.saveLog(model.booking_id, account_id,"", JsonConvert.SerializeObject(model), "SaveDrugAlergy").ConfigureAwait(false);
        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp replacePrescription(ReplacePrescriptionDto dto) {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isEmpty(booking)) {
            return error("Booking invalid");
        }
        //校验如果不是问诊中 - 返回错误信息
        if (!bookingBasicService.isInConsultation(booking.getId())) {
            //调用方法根据状态获取不同的错误信息返回
            return error(getErrorBookingValid(booking.getStatus()));
        }
        if (3 == booking.getStatus() && 1 == booking.getSubStatus()) {
            return error("Sorry, this consultation has been ended. Cannot update prescription.");
        }
        List<ReplacePrescriptionDto.Prescription> prescriptions = null;
        List<String> productIds = null;
        if (ObjectUtil.isNotEmpty(dto.getPrescriptions())) {
            prescriptions = dto.getPrescriptions();
            //获取出药品id集合
            productIds = prescriptions.stream().map(ReplacePrescriptionDto.Prescription::getId).collect(Collectors.toList());
        }
        //对方函数写的患者国家id为203才去校验这个药物
        if (booking.getPatientCountryOfResidence().equals("203")) {
            //校验药物
            if (ObjectUtil.isNotEmpty(dto.getPrescriptions())) {
                //查询药品统计 - 这里查询的是传入的药品id只要有一个不存在数据库中 -则返回错误信息
                Integer productCount = tblProductMapper.findCountByStatusIsTrueByIds(productIds);
                if (productCount > 0) {
                    return error("Medication Not Found.");
                }
            }
        }
        //统计用于后续判断
        long countByProductId = 0;
        if (booking.getPatientCountryOfResidence().equals("106")) {
            countByProductId = prescriptions.stream()
                    .filter(p -> p.getProduct_id() != null)
                    .map(ReplacePrescriptionDto.Prescription::getProduct_id)
                    .distinct()
                    .count();
        } else {
            countByProductId = prescriptions.stream()
                    .filter(p -> !p.getIs_package() && (p.getStep_dose() == null || p.getStep_dose().isEmpty()))
                    .map(ReplacePrescriptionDto.Prescription::getId)
                    .distinct()
                    .count();
        }

        /**
         * todo 药品数量不够时没有操作
         * 数量不够的返回：其中 Gauze 是药品的name,Quantity left = 74 Tablet是数量 MDMedication_qty_remain_in_stock
         * {
         *     "errorCode": 400,
         *     "message": "The quantity of Gauze in the inventory is not enough for your prescription.<br><strong>Quantity left = 74 Tablet</strong>",
         *     "data": null
         * }
         */
        //检查数量之类的
        if (ObjectUtil.isNotEmpty(prescriptions)) {
            List<ReplacePrescriptionDto.Prescription> checkProduct = processPrescriptions(prescriptions);
            //第一个检查
            List<CheckProductVo> count = tblProductMapper.findCheckByProduct(checkProduct, booking.getId());
            if (!count.isEmpty()) {
                //第一个检查的返回错误信息
                return error(getErrorStrByCheckProductVoList(count));
            }
            //第二个检查
            List<CheckProductVo> twoCheck = tblProductMapper.findCheckTwo(checkProduct, booking.getId());
            // 使用stream流计算quantityAvailable的合计
            BigDecimal sum = twoCheck.stream()
                    .map(checkProductVo -> checkProductVo.getQuantityAvailable() != null ?
                            checkProductVo.getQuantityAvailable() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            // 检查合计是否小于0
            if (sum.compareTo(BigDecimal.ZERO) < 0) {
                //第二个检查的返回错误信息
                return error(getErrorStrByCheckProductVoList(twoCheck));
            }
            //统计数量用于判断是否返回错误信息
            long countNumber = prescriptions.stream()
                    .filter(p -> !p.getIs_package() && (p.getStep_dose() == null || p.getStep_dose().isEmpty()))
                    .count();

            //检查数量
            if ((countNumber - countByProductId) > 0) {
                return error("This drug already been prescribed. To change the prescription, you can use the Edit (pencil) button.");
            }

            //todo 后续需要确认是否正确
            String memberId = booking.getMemId();
            if (booking.getMemType() == 2) {
                memberId = booking.getPatientMemId();
            }
            //第三个检查查询
            List<String> productNames = tblProductMapper.findCheckThree(checkProduct, memberId);
            //如果存在则返回错误信息
            if (!productNames.isEmpty()) {
                StringBuilder builder = new StringBuilder();
                productNames.forEach(item -> {
                    builder.append(item).append(",");
                });
                // 删除最后一个逗号
                if (builder.length() > 0) {
                    builder.deleteCharAt(builder.length() - 1);
                }
                return error("Medication(s) " + builder.toString() + " has been refill before.");
            }
            //校验unit
            List<Integer> unitList = prescriptions.stream()
                    .filter(p -> p.getUnit() != null)
                    .map(ReplacePrescriptionDto.Prescription::getUnit)
                    .collect(Collectors.toList());
            if (ObjectUtil.isNotEmpty(unitList)) {
                Integer countByUnitIds = tblUnitOfMeasurementMapper.findCountByIdsNotIn(unitList);
                if (countByUnitIds > 0) {
                    return error("UOM unit could not be found.");
                }
            }
            //校验durationUnit
            List<Integer> durationUnitList = prescriptions.stream()
                    .filter(p -> p.getDuration_unit() != null)
                    .map(ReplacePrescriptionDto.Prescription::getDuration_unit)
                    .collect(Collectors.toList());
            if (!durationUnitList.isEmpty()) {
                Integer countByDurationUnitIds = tblDurationUnitMapper.findCountByIdsNotIn(durationUnitList);
                if (countByDurationUnitIds > 0) {
                    return error("Duration unit could not be found.");
                }
            }

            //获取不等于的BookingPrescription
            List<String> lastProductIds = tblBookingPrescriptionMapper.findProductIdsByProductIdsNotInAndByBookingId(productIds, booking.getId());
            // 将 lastProductIds 转换成 Set 以提高查找效率
            Set<String> productIdSet = lastProductIds.stream().collect(Collectors.toSet());

            // 按照 id 进行过滤并统计 quantity 的总和
            BigDecimal totalQuantity = prescriptions.stream()
                    .filter(p -> productIdSet.contains(p.getId())) // 只考虑在 lastProductIds 中的 Prescription 对象
                    .map(ReplacePrescriptionDto.Prescription::getQuantity)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);

            //通过查看对方函数 - 这两个方法在下方两个if中都使用了 - 所以直接提取出来
            //当前时间
            Date CurrentTime = new Date();
            //expired batch and expired soon batch
            //采购，退换货订单表，在退换货中，exchange date 就是创建时间
            List<CheckPurchaseOrder> tableExpireSoon = tblPurchaseOrderDetailMapper.findCheckByProductIdAndByCurrentTime(lastProductIds, CurrentTime);

            //batches were expired
            List<CheckPurchaseOrder> tableExpired = tblPurchaseOrderDetailMapper.findCheckExpireByProductIdAndCurrentTime(lastProductIds, CurrentTime);

            //在线问诊
            if (1 == booking.getConsultationMode()) {
                //这里面几个函数都是相同的
                /**
                 * todo 进来就执行了一句调用函数， sp_UpdateMedicationInventoryByPrescription 在很多地方都见到过，等看看有没有公共方法
                 * Begin --- Import Medication In Inventory ---
                 * 				Exec [dbo].[sp_UpdateMedicationInventoryByPrescription] @PRM_booking_id, @prescriptions, 2
                 */

                /**
                 * 然后又调用了两个函数
                 * 			Begin --- Replace tbl_Booking_Prescription ---
                 * 				Exec [dbo].[API_ReplacePrescription] @PRM_booking_id, @PRM_prescription_status, @PRM_prescriptions,@PRM_package_prescriptions
                 * 			End
                 *
                 * 			Begin --- Export Medication From Inventory ---
                 * 				Exec [dbo].[sp_UpdateMedicationInventoryByPrescription] @PRM_booking_id, @prescriptions, -2
                 * 			End
                 * 		End
                 * 	todo 至此这个if结束
                 */

            } else {
                /**
                 * todo 这里面只调用了一个函数
                 * Begin --- Replace tbl_Booking_Prescription ---
                 * 				Exec [dbo].[API_ReplacePrescription] @PRM_booking_id, @PRM_prescription_status, @PRM_prescriptions,@PRM_package_prescriptions
                 * 			End
                 */
            }
            //查询总量 - 根据bookingId
            BigDecimal bookingTotalCostConsulation = tblBookingConsultationMapper.findSumAmountByBookingId(booking.getId());
            //查询practice_fee
            BigDecimal practiceFee = tblBookingFeeMapper.findPracticeFeeByBookingId(booking.getId());
            //计算和
            BigDecimal standardFee = bookingTotalCostConsulation.add(practiceFee);

            /**
             * todo 最后做的一个update操作，但是修改的这几个字段我们表都没有了 - 这里就不操作修改了
             * 		Update tbl_Booking
             * 		Set
             * 			booking_corporate_payment_pharma = @booking_corporate_payment_pharma,
             * 			booking_corporate_payment_total_amount = (ISNULL(booking_corporate_payment_consultation, 0) + @booking_corporate_payment_pharma), update_on = [dbo].[fn_Add_Hours]()
             * 		Where booking_id = @PRM_booking_id
             */
            //根据id查询药品名称集合
            List<ReplacePrescriptionFindProductVo> dbProductNames = tblProductMapper.findProductNameByIds(lastProductIds);
            //对方只取了第一个集合
            ReplacePrescriptionFindProductVo productVo = dbProductNames.get(0);
            //返回的字符串拼接
            StringBuilder expiredBatches = new StringBuilder();
            StringBuilder expriringSoonBatches = new StringBuilder();
            expiredBatches.append("<strong>Expired:</strong><ul>");
            //格式化时间
            SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");

            for (CheckPurchaseOrder order : tableExpired) {
                if (ObjectUtil.isNotEmpty(order.getExpirationDate())) {
                    expiredBatches.append("<li>")
                            .append(dateFormat.format(order.getExpirationDate()))
                            .append(order.getQuantityRemain() != null ? order.getQuantityRemain() : "")
                            .append(" ")
                            .append(productVo.getUQTName() != null ? productVo.getUQTName() : "")
                            .append("</li>");
                }
            }
            expiredBatches.append("</ul>");
            expriringSoonBatches.append("<strong>Expiring soon:</strong><ul>");
            for (CheckPurchaseOrder order : tableExpireSoon) {
                if (ObjectUtil.isNotEmpty(order.getExpirationDate())) {
                    expriringSoonBatches.append("<li>")
                            .append(dateFormat.format(order.getExpirationDate()))
                            .append(order.getQuantityRemain() != null ? order.getQuantityRemain() : "")
                            .append(" ")
                            .append(productVo.getUQTName() != null ? productVo.getUQTName() : "")
                            .append("</li>");
                }
            }
            expriringSoonBatches.append("</ul>");


            /**
             * todo 剩余一个websocket
             * System.Threading.Thread threadSocket = new System.Threading.Thread(delegate ()
             *                 {
             *                     //Do not change doctor.updateprescription even though Method Name Changed
             *                     member_bl.CallWebSocket(model.booking_id, "doctor.updateprescription", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
             *                 });
             *                 threadSocket.IsBackground = true;
             *                 threadSocket.Start();
             */

            return success("Expiry notice for "
                    + productVo.getProductName()
                    + ".&lt;br&gt;&lt;br&gt;&lt;strong&gt;Available quantity = "
                    + productVo.getQtyRemainInStock()
                    + "&lt;/strong&gt;&lt;br&gt;&lt;br&gt;"
                    + expiredBatches.toString()
                    + expriringSoonBatches.toString());
        }

        return error();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp repeatDiagnosisAndMedication(RepeatDiagnosisAndMedicationDto dto) {
        Kit.allParamValid(dto,"bookingId","current_bookingId");
        if(StringUtils.isBlank(dto.getMeds())||StringUtils.isBlank(dto.getDiagnosis())||StringUtils.isBlank(dto.getPackages())){
            return error();
        }
        //删除重复药品、诊断的repeat
        repeatMapper.deleteDuplicateRepeat(dto);
        //添加repeat
        List<TblDoctorRepeat> repeatList = new ArrayList<>();

        //添加药
        TblBookingPrescription condition = new TblBookingPrescription();
        condition.setDelFlag(0);
        condition.setBookingId(dto.getBookingId());
        if(StringUtils.isNotBlank(dto.getMeds())){
            String[] medsArr = dto.getMeds().split(",");
            for (String s : medsArr) {
                TblDoctorRepeat tempRepeat = new TblDoctorRepeat();
                tempRepeat.setType(2);
                tempRepeat.setBookingId(dto.getBookingId());
                tempRepeat.setCurrentBookingId(dto.getCurrent_bookingId());
                tempRepeat.setMedId(s);
                tempRepeat.setIsComplete(1);
                repeatList.add(tempRepeat);

                condition.setProductId(s);
                List<TblBookingPrescription> pList = tblBookingPrescriptionMapper.select(condition);
                TblBookingPrescription prescription = new TblBookingPrescription();
                for (TblBookingPrescription p : pList) {
                    BeanUtils.copyProperties(p,prescription);
                    prescription.setBookingId(dto.getCurrent_bookingId());
                    prescription.setSelected(0);
                    prescription.setInExclusionList(null);
                    prescription.setCoveredDiagnosis(null);
                    prescription.setCoveredDiagnosisRegex(null);
                    prescription.setCoveredDiagnosisCode(null);
                    prescription.setMedicationApprovedAt(null);
                    prescription.setMedicationApprovedBy(null);
                    prescription.setId(null);
                    prescription.setCreatedOnUtc(null);
                    prescription.setCreatedBy(null);
                    prescription.setUpdatedBy(null);
                    prescription.setUpdatedOnUtc(null);

                    tblBookingPrescriptionMapper.insertSelective(prescription);
                }

            }
        }

        //添加package
        if(StringUtils.isNotBlank(dto.getPackages())){
            String[] medsArr = dto.getMeds().split(",");
            for (String s : medsArr) {
                TblDoctorRepeat tempRepeat = new TblDoctorRepeat();
                tempRepeat.setType(3);
                tempRepeat.setBookingId(dto.getBookingId());
                tempRepeat.setCurrentBookingId(dto.getCurrent_bookingId());
                tempRepeat.setMedId(s);
                tempRepeat.setIsComplete(1);
                repeatList.add(tempRepeat);

                condition.setPackageId(s);
                List<TblBookingPrescription> pList = tblBookingPrescriptionMapper.select(condition);
                TblBookingPrescription prescription = new TblBookingPrescription();
                for (TblBookingPrescription p : pList) {
                    BeanUtils.copyProperties(p,prescription);
                    prescription.setBookingId(dto.getCurrent_bookingId());
                    prescription.setSelected(0);
                    prescription.setInExclusionList(null);
                    prescription.setCoveredDiagnosis(null);
                    prescription.setCoveredDiagnosisRegex(null);
                    prescription.setCoveredDiagnosisCode(null);
                    prescription.setMedicationApprovedAt(null);
                    prescription.setMedicationApprovedBy(null);
                    prescription.setId(null);
                    prescription.setCreatedOnUtc(null);
                    prescription.setCreatedBy(null);
                    prescription.setUpdatedBy(null);
                    prescription.setUpdatedOnUtc(null);

                    tblBookingPrescriptionMapper.insertSelective(prescription);
                }
            }
        }

        //添加diagnosis
        if(StringUtils.isNotBlank(dto.getDiagnosis())){
            String[] medsArr = dto.getMeds().split(",");

            JSONArray arr = new JSONArray();
            for (String s : medsArr) {
                TblDoctorRepeat tempRepeat = new TblDoctorRepeat();
                tempRepeat.setType(1);
                tempRepeat.setBookingId(dto.getBookingId());
                tempRepeat.setCurrentBookingId(dto.getCurrent_bookingId());
                tempRepeat.setDiagnosisId(s);
                tempRepeat.setIsComplete(1);
                repeatList.add(tempRepeat);

                TblBookingDiagnosis tblBookingDiagnosis = new TblBookingDiagnosis();
                tblBookingDiagnosis.setBookingId(dto.getCurrent_bookingId());
                tblBookingDiagnosis.setDelFlag(0);
                tblBookingDiagnosis.setInExclusionList(null);
                tblBookingDiagnosis.setDiagnosisId(s);
                tblBookingDiagnosisMapper.insertSelective(tblBookingDiagnosis);

                JSONObject dJson = new JSONObject();
                dJson.put("type",1);
                dJson.put("Diagnosis_ID",s);
                arr.add(dJson);
            }
            //添加diagnosis log
            TblBookingDiagnosisLog log = new TblBookingDiagnosisLog();
            log.setRepeat(1);
            log.setBookingId(dto.getCurrent_bookingId());
            log.setJson(arr.toString());
            tblBookingDiagnosisLogMapper.insertSelective(log);
        }

        return patientHistoryService.getBookingInfo(dto.getCurrent_bookingId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp requestFileFromPatient(String bookingId) {
        if (ObjectUtil.isEmpty(bookingId)) {
            return error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(bookingId);
        if (ObjectUtil.isNull(booking)) {
            return error("Booking invalid");
        }
        //这个接口对方并没有校验是否问诊之类的
        //UPDATE tbl_Booking Set update_on = dbo.fn_Add_Hours(), is_request_upload_file = 1 WHERE booking_id = '{bookingId}'
        //我们的is_request_upload_file在tbl_booking_summary
        tblBookingSummaryMapper.updateRequestUploadFileTrueByBookingId(booking.getId());
        //todo member_BL.APISocket_SendMessageWebSocket(bookingId, "status_changed", null, "doctor.requestuploadfile");
        /**
         * APISocket_SendMessageWebSocket进来之后的代码
         *public void APISocket_SendMessageWebSocket(string booking_id, string event_name, object event_data, string event_action)
         *         {
         *             System.Threading.Thread thread = new System.Threading.Thread(delegate ()
         *             {
         *                 dynamic res = new System.Dynamic.ExpandoObject();
         *                 res.room_id = booking_id;
         *                 res.event_name = event_name;
         *                 res.event_action = event_action;
         *                 res.event_data = event_data;
         *
         *                 var jvalue = new JObject();
         *                 jvalue.Add("booking_id", new JValue(booking_id));
         *                 jvalue.Add("response", new JValue(JsonConvert.SerializeObject(res)));
         *
         *                 var restClient = new RestClient(Constants.SOCKET_URL);
         *                 var request = new RestRequest("Socket/SendMessageWebSocket", Method.POST);
         *                 request.AddParameter("application/json", jvalue, ParameterType.RequestBody);
         *                 request.AddHeader("Authorization", Constants.SOCKET_AUTHORIZATION);
         *                 // execute the request
         *                 var response = restClient.Execute(request);
         *             });
         *             thread.IsBackground = true;
         *             thread.Start();
         *         }
         */
        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp updateDiagnosisAllowPatientToClaim(UpdateDiagnosisAllowPatientToClaimDto dto) {
        //对方的做法只是检查了bookingId如果有值 - 直接执行了一个sql 然后返回成功 - 如果bookingId没值也返回的成功 - 这里直接复刻
        if (ObjectUtil.isNotEmpty(dto.getBooking_id())) {
            //如果这个字段值为null也不操作
            if (ObjectUtil.isNotNull(dto.getDiagnosis_allow_patient_to_claim())) {
                Integer diagnosisAllowPatientToClaim = dto.getDiagnosis_allow_patient_to_claim() ? 1 : 0;
                tblBookingSummaryMapper.updateDiagnosisAllowPatientToClaimByBookingId(diagnosisAllowPatientToClaim, dto.getBooking_id());
            }
        }
        return success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResultApp startConsulation(StartConsulationDto dto) throws ParseException {
        if (ObjectUtil.isEmpty(dto.getBooking_id())) {
            return error("The booking_id field is required.");
        }
        TblBooking booking = tblBookingMapper.findById(dto.getBooking_id());
        if (ObjectUtil.isNull(booking)) {
            return error("Booking invalid");
        }
        //根据状态返回不同的错误信息
        if (1 != booking.getStatus() && 3 != booking.getAppointmentType()) {
            return error(getErrorBookingValid(booking.getStatus()));
        }
        TblDoctor doctor = UserUtil.getCurLoginDoctor();
        Integer count = tblBookingQueueMapper.findCountByDoctorIdAndByNotBookingId(doctor.getId(), booking.getId());
        String currentBookingId = tblBookingQueueMapper.findCurrentBookingIdByDoctorId(doctor.getId());
        if (count > 1 && 3 != booking.getAppointmentType()) {
            return error("There is a booking still in processing.");
        }
        if (currentBookingId.equals(booking.getId()) && count > 0 && 3 != booking.getAppointmentType()) {
            return error("There is a booking still in processing.");
        }
        //检查booking的诊所
        if (ObjectUtil.isEmpty(booking.getClinicId())) {
            if (ObjectUtil.isNotEmpty(doctor.getCurrentClinicId())) {
                booking.setClinicId(doctor.getCurrentClinicId());
            }
        }
        //检查公司
        if (ObjectUtil.isEmpty(booking.getCompanyId())) {
            if (ObjectUtil.isNotEmpty(doctor.getCompanyId())) {
                booking.setCompanyId(doctor.getCompanyId());
            }
        }
        //修改booking
        tblBookingMapper.updateByPrimaryKeySelective(booking);
        //设置队列
        tblBookingQueueMapper.updateQueueNoNullByBookingId(booking.getId());
        //当前时间
        Date date = new Date();
        Date utcDate = Kit.getUtcDate();
        //根据不同判断做不通事情
        if (1 == booking.getStatus() && -1 == booking.getSubStatus()) {
            tblBookingConsultationMapper.updateTimeByBooking(date, utcDate, booking.getId());
        } else {
            if (5 == booking.getConsultationMode()) {
                // [dbo].[sp_UpdateBookingStatus] @PRM_booking_id, 3, 0
                // todo 司机相关跳过 - 并且对方函数也没做什么事情，最终也就是保存了一个log而已
//                spUpdateBookingStatus();
                booking.setAppointmentEndTime(new Date());
                tblBookingMapper.updateByPrimaryKeySelective(booking);
            } else {
                //[dbo].[sp_UpdateBookingStatus] @PRM_booking_id, 2, 0 --> booking_status: Ready
                // todo 上方函数跳过
            }
        }

        UpdateQueueNoAfterConnected(booking, null);
        //todo 只差一个返回值示例
        //以及member_bl.SendMessageAndResetQueueNo(model.booking_id, "doctor.startconsulation", Common.GetHeaderValue(Request, Constants.HEADER_TIMEZONE));
        //booking_bl.saveLog(model.booking_id, account_id,"", JsonConvert.SerializeObject(model), "StartConsulation").ConfigureAwait(false);
        return success();
    }

    /**
     * 函数： UpdateQueueNoAfterConnected
     * @param booking
     * @param deleteFrom
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void UpdateQueueNoAfterConnected(TblBooking booking, Integer deleteFrom) {
        //当前booking的医生
        TblDoctor tblDoctor = tblDoctorMapper.selectByPrimaryKey(booking.getDoctorId());
        //查询队列
        TblBookingQueue queue = tblBookingQueueMapper.findOneByBookingIdAndByStatus(booking.getId());
        if (2 == booking.getStatus() || 3 == booking.getStatus()) {
            //doctor ready
            queue.setPositionNo(0);
            if (ObjectUtil.isEmpty(queue.getDepartedOn())) {
                queue.setDepartedOn(new Date());
            }
            tblBookingQueueMapper.updateByPrimaryKeySelective(queue);
        } else if (-1 == booking.getStatus()) {
            //patient cancel
            if (ObjectUtil.isEmpty(deleteFrom)) {
                deleteFrom = -5;
                // todo 日志 EXEC [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 100, @PRM_delete_from, '', null, null
//                insertBookingLog();
            }
        } else if (-6 == booking.getStatus()) {
            //patient disconnect
            //Remove queue
            tblBookingQueueMapper.deleteByBookingId(booking.getId());
            if (ObjectUtil.isEmpty(deleteFrom)) {
                deleteFrom = -7;
                // todo 日志 EXEC [dbo].[API_Insert_Booking_Log] @PRM_booking_id, 100, @PRM_delete_from, '', null, null
                //insertBookingLog();
            }
        }
        //判断队列展示位置
        if (ObjectUtil.isNotEmpty(queue.getPositionNo()) && queue.getPositionNo() > 0) {
            //根据GP和SP做不同业务
            tblBookingQueueMapper.updatePositionNoMinusOneByDoctorId(tblDoctor.getId(), queue.getPositionNo());
        }

    }

//    public void spUpdateBookingStatus(String bookingId, Integer status, Integer subStatus) {
//        TblBooking booking = tblBookingMapper.selectByPrimaryKey(bookingId);
//        // 实际上对方这里判断了这个 99 != booking.getStatus()
//        // 然后else 但是操作的sql一模一样，所以这里不判断了
//        // 而且实际上对方修改的这一句sql都不需要修改 - 因为修改的参数也是当前的sql的status和subStatus的参数传入的这个方法
////        tblBookingMapper.updateStatusAndSubStatusByBookingId(bookingId, status, subStatus);
//
//
//    }
//
//    public void insertBookingLog(String bookingId,Integer status,Integer subStatus,){
//
//    }


    /**
     * 传入参数并拼接返回值
     *
     * @param list 集合
     * @return
     */
    public String getErrorStrByCheckProductVoList(List<CheckProductVo> list) {
        StringBuilder builderName = new StringBuilder();
        StringBuilder builderQuantityLeft = new StringBuilder();
        StringBuilder builderQuantityType = new StringBuilder();
        for (CheckProductVo vo : list) {
            builderName.append(vo.getName()).append(",");
            builderQuantityLeft.append(vo.getQuantityLeft()).append(",");
            builderQuantityType.append(vo.getQuantityType()).append(",");
        }
        // 删除最后一个逗号
        if (builderName.length() > 0) {
            builderName.deleteCharAt(builderName.length() - 1);
        }
        if (builderQuantityLeft.length() > 0) {
            builderQuantityLeft.deleteCharAt(builderQuantityLeft.length() - 1);
        }
        if (builderQuantityType.length() > 0) {
            builderQuantityType.deleteCharAt(builderQuantityType.length() - 1);
        }
        return "The quantity of " + builderName.toString()
                + " in the inventory is not enough for your prescription.&lt;br&gt;&lt;strong&gt;Quantity left = "
                + builderQuantityLeft.toString() + " " + builderQuantityType.toString() + "&lt;/strong&gt;";
    }


    // 方法用于处理处方列表
    public List<ReplacePrescriptionDto.Prescription> processPrescriptions(List<ReplacePrescriptionDto.Prescription> prescriptions) {
        // 使用流按照id分组，并计算每组的quantity总和
        Map<String, BigDecimal> idQuantitySumMap = prescriptions.stream()
                .collect(Collectors.groupingBy(ReplacePrescriptionDto.Prescription::getId,
                        Collectors.mapping(p -> p.getQuantity() != null ? p.getQuantity() : BigDecimal.ZERO,
                                Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))));

        // 将Map转换为Prescription对象列表
        List<ReplacePrescriptionDto.Prescription> sums = idQuantitySumMap.entrySet().stream()
                .map(entry -> {
                    ReplacePrescriptionDto.Prescription sumPrescription = new ReplacePrescriptionDto.Prescription();
                    sumPrescription.setId(entry.getKey());
                    sumPrescription.setQuantity(entry.getValue());
                    return sumPrescription;
                })
                .collect(Collectors.toList());

        return sums;
    }


    /**
     * 根据不同状态获取不同的返回message
     *
     * @param bookingStatus booking表的status
     * @return 错误信息
     */
    @Override
    public String getErrorBookingValid(Integer bookingStatus) {
        String errorString;
        switch (bookingStatus) {
            case -5:
                errorString = "Sorry, this delivery has been indicated as QR code invalid.";
                break;
            case -4:
                errorString = "Sorry, this delivery has been indicated as IC invalid.";
                break;
            case -3:
                errorString = "Sorry, this delivery has been indicated as patient not home.";
                break;
            case -2:
                errorString = "Sorry, this delivery has been indicated as patient uncontactable.";
                break;
            case -1:
                errorString = "Sorry, this booking has been cancelled.";
                break;
            case 99:
                errorString = "Sorry, this booking has been completed.";
                break;
            case 1:
                errorString = "Sorry, this booking haven't started.";
                break;
            case 4:
                errorString = "Sorry, this booking has been consulted.";
                break;
            case 5:
            case 6:
            case 7:
            default:
                errorString = "Booking invalid.";
                break;
        }

        return errorString;
    }

}
